net: if: vlan: Add virtual lan support

This allows creation of virtual lan (VLAN) networks. VLAN support is
only available for ethernet network technology.

Fixes #3234

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2018-01-19 12:24:33 +02:00 committed by Anas Nashif
parent 700e4bd2e8
commit de13e979fc
17 changed files with 935 additions and 89 deletions

View file

@ -15,10 +15,13 @@
#include <zephyr/types.h>
#include <stdbool.h>
#include <atomic.h>
#include <net/net_ip.h>
#include <net/net_pkt.h>
#include <misc/util.h>
#include <net/net_if.h>
#include <net/ethernet_vlan.h>
#ifdef __cplusplus
extern "C" {
@ -36,6 +39,7 @@ extern "C" {
#define NET_ETH_PTYPE_ARP 0x0806
#define NET_ETH_PTYPE_IP 0x0800
#define NET_ETH_PTYPE_IPV6 0x86dd
#define NET_ETH_PTYPE_VLAN 0x8100
#define NET_ETH_MINIMAL_FRAME_SIZE 60
@ -45,6 +49,9 @@ enum eth_hw_caps {
/** RX Checksum offloading supported */
ETH_HW_RX_CHKSUM_OFFLOAD = BIT(1),
/** VLAN supported */
ETH_HW_VLAN = BIT(2),
};
struct ethernet_api {
@ -56,6 +63,13 @@ struct ethernet_api {
/** Get the device capabilities */
enum eth_hw_caps (*get_capabilities)(struct device *dev);
/** The IP stack will call this function when a VLAN tag is enabled
* or disabled. If enable is set to true, then the VLAN tag was added,
* if it is false then the tag was removed. The driver can utilize
* this information if needed.
*/
int (*vlan_setup)(struct net_if *iface, u16_t tag, bool enable);
};
struct net_eth_addr {
@ -68,6 +82,70 @@ struct net_eth_hdr {
u16_t type;
} __packed;
struct ethernet_vlan {
/** Network interface that has VLAN enabled */
struct net_if *iface;
/** VLAN tag */
u16_t tag;
};
#if defined(CONFIG_NET_VLAN_COUNT)
#define NET_VLAN_MAX_COUNT CONFIG_NET_VLAN_COUNT
#else
/* Even thou there are no VLAN support, the minimum count must be set to 1.
*/
#define NET_VLAN_MAX_COUNT 1
#endif
/** Ethernet L2 context that is needed for VLAN */
struct ethernet_context {
#if defined(CONFIG_NET_VLAN)
struct ethernet_vlan vlan[NET_VLAN_MAX_COUNT];
/** Array that will help when checking if VLAN is enabled for
* some specific network interface. Requires that VLAN count
* NET_VLAN_MAX_COUNT is not smaller than the actual number
* of network interfaces.
*/
ATOMIC_DEFINE(interfaces, NET_VLAN_MAX_COUNT);
/** Flag that tells whether how many VLAN tags are enabled for this
* context. The same information can be dug from the vlan array but
* this saves some time in RX path.
*/
s8_t vlan_enabled;
/** Is this context already initialized */
bool is_init;
#endif
};
#define ETHERNET_L2_CTX_TYPE struct ethernet_context
/**
* @brief Initialize Ethernet L2 stack for a given interface
*
* @param iface A valid pointer to a network interface
*/
void ethernet_init(struct net_if *iface);
#if defined(CONFIG_NET_VLAN)
/* Separate header for VLAN as some of device interfaces might not
* support VLAN.
*/
struct net_eth_vlan_hdr {
struct net_eth_addr dst;
struct net_eth_addr src;
struct {
u16_t tpid; /* tag protocol id */
u16_t tci; /* tag control info */
} vlan;
u16_t type;
} __packed;
#endif /* CONFIG_NET_VLAN */
static inline bool net_eth_is_addr_broadcast(struct net_eth_addr *addr)
{
if (addr->addr[0] == 0xff &&
@ -133,6 +211,116 @@ enum eth_hw_caps net_eth_get_hw_capabilities(struct net_if *iface)
return eth->get_capabilities(net_if_get_device(iface));
}
#if defined(CONFIG_NET_VLAN)
/**
* @brief Add VLAN tag to the interface.
*
* @param iface Interface to use.
* @param tag VLAN tag to add
*
* @return 0 if ok, <0 if error
*/
int net_eth_vlan_enable(struct net_if *iface, u16_t tag);
/**
* @brief Remove VLAN tag from the interface.
*
* @param iface Interface to use.
* @param tag VLAN tag to remove
*
* @return 0 if ok, <0 if error
*/
int net_eth_vlan_disable(struct net_if *iface, u16_t tag);
/**
* @brief Return VLAN tag specified to network interface
*
* @param iface Network interface.
*
* @return VLAN tag for this interface or NET_VLAN_TAG_UNSPEC if VLAN
* is not configured for that interface.
*/
u16_t net_eth_get_vlan_tag(struct net_if *iface);
/**
* @brief Return network interface related to this VLAN tag
*
* @param iface Master network interface. This is used to get the
* pointer to ethernet L2 context
* @param tag VLAN tag
*
* @return Network interface related to this tag or NULL if no such interface
* exists.
*/
struct net_if *net_eth_get_vlan_iface(struct net_if *iface, u16_t tag);
/**
* @brief Check if VLAN is enabled for a specific network interface.
*
* @param ctx Ethernet context
* @param iface Network interface
*
* @return True if VLAN is enabled for this network interface, false if not.
*/
bool net_eth_is_vlan_enabled(struct ethernet_context *ctx,
struct net_if *iface);
#define ETH_NET_DEVICE_INIT(dev_name, drv_name, init_fn, \
data, cfg_info, prio, api, mtu) \
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, \
cfg_info, POST_KERNEL, prio, api); \
NET_L2_DATA_INIT(dev_name, 0, NET_L2_GET_CTX_TYPE(ETHERNET_L2)); \
NET_IF_INIT(dev_name, 0, ETHERNET_L2, mtu, NET_VLAN_MAX_COUNT)
#else /* CONFIG_NET_VLAN */
#define ETH_NET_DEVICE_INIT(dev_name, drv_name, init_fn, \
data, cfg_info, prio, api, mtu) \
NET_DEVICE_INIT(dev_name, drv_name, init_fn, \
data, cfg_info, prio, api, ETHERNET_L2, \
NET_L2_GET_CTX_TYPE(ETHERNET_L2), mtu)
static inline int net_eth_vlan_enable(struct net_if *iface, u16_t vlan_tag)
{
return -EINVAL;
}
static inline int net_eth_vlan_disable(struct net_if *iface, u16_t vlan_tag)
{
return -EINVAL;
}
static inline u16_t net_eth_get_vlan_tag(struct net_if *iface)
{
return NET_VLAN_TAG_UNSPEC;
}
static inline
struct net_if *net_eth_get_vlan_iface(struct net_if *iface, u16_t tag)
{
return NULL;
}
#endif /* CONFIG_NET_VLAN */
/**
* @brief Fill ethernet header in network packet.
*
* @param ctx Ethernet context
* @param pkt Network packet
* @param frag Ethernet header in packet
* @param ptype Upper level protocol type (in network byte order)
* @param src Source ethernet address
* @param dst Destination ethernet address
*
* @return Pointer to ethernet header struct inside net_buf.
*/
struct net_eth_hdr *net_eth_fill_header(struct ethernet_context *ctx,
struct net_pkt *pkt,
struct net_buf *frag,
u32_t ptype,
u8_t *src,
u8_t *dst);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,76 @@
/** @file
* @brief VLAN specific definitions.
*
* Virtual LAN specific definitions.
*/
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ETHERNET_VLAN_H
#define __ETHERNET_VLAN_H
/**
* @brief VLAN definitions and helpers
* @defgroup vlan Virtual LAN definitions and helpers
* @ingroup networking
* @{
*/
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NET_VLAN_TAG_UNSPEC 0x0fff
/* Get VLAN identifier from TCI */
static inline u16_t net_eth_vlan_get_vid(u16_t tci)
{
return tci & 0x0fff;
}
/* Get Drop Eligible Indicator from TCI */
static inline u8_t net_eth_vlan_get_dei(u16_t tci)
{
return (tci >> 12) & 0x01;
}
/* Get Priority Code Point from TCI */
static inline u8_t net_eth_vlan_get_pcp(u16_t tci)
{
return (tci >> 13) & 0x07;
}
/* Set VLAN identifier to TCI */
static inline u16_t net_eth_vlan_set_vid(u16_t tci, u16_t vid)
{
return (tci & 0xf000) | (vid & 0x0fff);
}
/* Set Drop Eligible Indicator to TCI */
static inline u16_t net_eth_vlan_set_dei(u16_t tci, bool dei)
{
return (tci & 0xefff) | ((!!dei) << 12);
}
/* Set Priority Code Point to TCI */
static inline u16_t net_eth_vlan_set_pcp(u16_t tci, u8_t pcp)
{
return (tci & 0x1fff) | ((pcp & 0x07) << 13);
}
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* __ETHERNET_VLAN_H */

View file

@ -358,7 +358,6 @@ struct net_if_dev {
*/
struct net_offload *offload;
#endif /* CONFIG_NET_OFFLOAD */
};
/**
@ -1576,18 +1575,18 @@ struct net_if_api {
#define NET_IF_GET(dev_name, sfx) \
((struct net_if *)&NET_IF_GET_NAME(dev_name, sfx))
#define NET_IF_INIT(dev_name, sfx, _l2, _mtu) \
static struct net_if_dev (NET_IF_DEV_GET_NAME(dev_name, sfx)) __used \
__attribute__((__section__(".net_if_dev.data"))) = { \
#define NET_IF_INIT(dev_name, sfx, _l2, _mtu, _num_configs) \
static struct net_if_dev (NET_IF_DEV_GET_NAME(dev_name, sfx)) \
__used __attribute__((__section__(".net_if_dev.data"))) = { \
.dev = &(__device_##dev_name), \
.l2 = &(NET_L2_GET_NAME(_l2)), \
.l2_data = &(NET_L2_GET_DATA(dev_name, sfx)), \
.mtu = _mtu, \
}; \
static struct net_if \
(NET_IF_GET_NAME(dev_name, sfx))[NET_IF_MAX_CONFIGS] __used \
(NET_IF_GET_NAME(dev_name, sfx))[_num_configs] __used \
__attribute__((__section__(".net_if.data"))) = { \
[0 ... (NET_IF_MAX_CONFIGS - 1)] = { \
[0 ... (_num_configs - 1)] = { \
.if_dev = &(NET_IF_DEV_GET_NAME(dev_name, sfx)), \
NET_IF_CONFIG_INIT \
} \
@ -1601,7 +1600,7 @@ struct net_if_api {
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, \
cfg_info, POST_KERNEL, prio, api); \
NET_L2_DATA_INIT(dev_name, 0, l2_ctx_type); \
NET_IF_INIT(dev_name, 0, l2, mtu)
NET_IF_INIT(dev_name, 0, l2, mtu, NET_IF_MAX_CONFIGS)
/**
* If your network device needs more than one instance of a network interface,
@ -1614,7 +1613,7 @@ struct net_if_api {
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, \
cfg_info, POST_KERNEL, prio, api); \
NET_L2_DATA_INIT(dev_name, instance, l2_ctx_type); \
NET_IF_INIT(dev_name, instance, l2, mtu)
NET_IF_INIT(dev_name, instance, l2, mtu, NET_IF_MAX_CONFIGS)
#ifdef __cplusplus
}

View file

@ -32,6 +32,9 @@
extern "C" {
#endif
/* Specifying VLAN tag here in order to avoid circular dependencies */
#define NET_VLAN_TAG_UNSPEC 0x0fff
/** Protocol families */
#define PF_UNSPEC 0 /* Unspecified. */
#define PF_INET 2 /* IP protocol family. */

View file

@ -70,7 +70,6 @@ NET_L2_DECLARE_PUBLIC(DUMMY_L2);
#ifdef CONFIG_NET_L2_ETHERNET
#define ETHERNET_L2 ETHERNET
#define ETHERNET_L2_CTX_TYPE void*
NET_L2_DECLARE_PUBLIC(ETHERNET_L2);
#endif /* CONFIG_NET_L2_ETHERNET */

View file

@ -26,6 +26,7 @@
#include <net/net_ip.h>
#include <net/net_if.h>
#include <net/net_context.h>
#include <net/ethernet_vlan.h>
#ifdef __cplusplus
extern "C" {
@ -149,6 +150,15 @@ struct net_pkt {
*/
u8_t priority;
#endif
#if defined(CONFIG_NET_VLAN)
/* VLAN TCI (Tag Control Information). This contains the Priority
* Code Point (PCP), Drop Eligible Indicator (DEI) and VLAN
* Identifier (VID, called more commonly VLAN tag). This value is
* kept in host byte order.
*/
u16_t vlan_tci;
#endif /* CONFIG_NET_VLAN */
/* @endcond */
/** Reference counter */
@ -414,12 +424,87 @@ static inline u8_t net_pkt_priority(struct net_pkt *pkt)
{
return 0;
}
#endif
static inline void net_pkt_set_priority(struct net_pkt *pkt,
u8_t priority)
#if defined(CONFIG_NET_VLAN)
static inline u16_t net_pkt_vlan_tag(struct net_pkt *pkt)
{
return net_eth_vlan_get_vid(pkt->vlan_tci);
}
static inline void net_pkt_set_vlan_tag(struct net_pkt *pkt, u16_t tag)
{
pkt->vlan_tci = net_eth_vlan_set_vid(pkt->vlan_tci, tag);
}
static inline u8_t net_pkt_vlan_priority(struct net_pkt *pkt)
{
return net_eth_vlan_get_pcp(pkt->vlan_tci);
}
static inline void net_pkt_set_vlan_priority(struct net_pkt *pkt,
u8_t priority)
{
pkt->vlan_tci = net_eth_vlan_set_pcp(pkt->vlan_tci, priority);
}
static inline bool net_pkt_vlan_dei(struct net_pkt *pkt)
{
return net_eth_vlan_get_dei(pkt->vlan_tci);
}
static inline void net_pkt_set_vlan_dei(struct net_pkt *pkt, bool dei)
{
pkt->vlan_tci = net_eth_vlan_set_dei(pkt->vlan_tci, dei);
}
static inline void net_pkt_set_vlan_tci(struct net_pkt *pkt, u16_t tci)
{
pkt->vlan_tci = tci;
}
static inline u16_t net_pkt_vlan_tci(struct net_pkt *pkt)
{
return pkt->vlan_tci;
}
#else
static inline u16_t net_pkt_vlan_tag(struct net_pkt *pkt)
{
return NET_VLAN_TAG_UNSPEC;
}
static inline void net_pkt_set_vlan_tag(struct net_pkt *pkt, u16_t tag)
{
ARG_UNUSED(pkt);
ARG_UNUSED(priority);
ARG_UNUSED(tag);
}
static inline u8_t net_pkt_vlan_priority(struct net_pkt *pkt)
{
ARG_UNUSED(pkt);
return 0;
}
static inline bool net_pkt_vlan_dei(struct net_pkt *pkt)
{
return false;
}
static inline void net_pkt_set_vlan_dei(struct net_pkt *pkt, bool dei)
{
ARG_UNUSED(pkt);
ARG_UNUSED(dei);
}
static inline u16_t net_pkt_vlan_tci(struct net_pkt *pkt)
{
return NET_VLAN_TAG_UNSPEC; /* assumes priority is 0 */
}
static inline void net_pkt_set_vlan_tci(struct net_pkt *pkt, u16_t tci)
{
ARG_UNUSED(pkt);
ARG_UNUSED(tci);
}
#endif

View file

@ -24,6 +24,7 @@ config NET_INITIAL_TTL
config NET_IF_MAX_IPV4_COUNT
int "Max number of IPv4 network interfaces in the system"
default 1
default NET_VLAN_COUNT if NET_VLAN
help
This tells how many network interfaces there will be in the system
that will have IPv4 enabled.

View file

@ -18,6 +18,7 @@ if NET_IPV6
config NET_IF_MAX_IPV6_COUNT
int "Max number of IPv6 network interfaces in the system"
default 1
default NET_VLAN_COUNT if NET_VLAN
help
This tells how many network interfaces there will be in the system
that will have IPv6 enabled.

View file

@ -22,6 +22,22 @@ config NET_L2_ETHERNET
If NET_SLIP_TAP is selected, NET_L2_ETHERNET will enable to fully
simulate Ethernet through SLIP.
config NET_VLAN
bool "Enable virtual lan support"
default n
depends on NET_L2_ETHERNET
help
Enables virtual lan (VLAN) support for Ethernet.
config NET_VLAN_COUNT
int "Max VLAN tags supported in the system"
default 1
range 1 1 if SLIP
range 1 255
depends on NET_VLAN
help
How many VLAN tags can be configured.
config NET_DEBUG_L2_ETHERNET
bool "Debug Ethernet L2 layer"
default n

View file

@ -101,14 +101,24 @@ static inline struct net_pkt *prepare_arp(struct net_if *iface,
struct arp_entry *entry,
struct net_pkt *pending)
{
#if defined(CONFIG_NET_VLAN)
u16_t vlan_tag = net_eth_get_vlan_tag(iface);
#endif
struct ethernet_context *ctx = net_if_l2_data(iface);
int eth_hdr_len = sizeof(struct net_eth_hdr);
struct net_pkt *pkt;
struct net_buf *frag;
struct net_arp_hdr *hdr;
struct net_eth_hdr *eth;
struct in_addr *my_addr;
pkt = net_pkt_get_reserve_tx(sizeof(struct net_eth_hdr),
NET_BUF_TIMEOUT);
#if defined(CONFIG_NET_VLAN)
if (ctx->vlan_enabled && vlan_tag != NET_VLAN_TAG_UNSPEC) {
eth_hdr_len = sizeof(struct net_eth_vlan_hdr);
}
#endif
pkt = net_pkt_get_reserve_tx(eth_hdr_len, NET_BUF_TIMEOUT);
if (!pkt) {
return NULL;
}
@ -124,7 +134,13 @@ static inline struct net_pkt *prepare_arp(struct net_if *iface,
net_pkt_set_family(pkt, AF_INET);
hdr = NET_ARP_HDR(pkt);
eth = NET_ETH_HDR(pkt);
#if defined(CONFIG_NET_VLAN)
net_pkt_set_vlan_tag(pkt, vlan_tag);
#endif
eth = net_eth_fill_header(ctx, pkt, frag, htons(NET_ETH_PTYPE_ARP),
NULL, NULL);
/* If entry is not set, then we are just about to send
* an ARP request using the data in pending net_pkt.
@ -146,7 +162,6 @@ static inline struct net_pkt *prepare_arp(struct net_if *iface,
sizeof(struct net_eth_addr));
}
eth->type = htons(NET_ETH_PTYPE_ARP);
memset(&eth->dst.addr, 0xff, sizeof(struct net_eth_addr));
hdr->hwtype = htons(NET_ARP_HTYPE_ETH);
@ -181,8 +196,9 @@ static inline struct net_pkt *prepare_arp(struct net_if *iface,
struct net_pkt *net_arp_prepare(struct net_pkt *pkt)
{
struct net_buf *frag;
struct ethernet_context *ctx = net_if_l2_data(net_pkt_iface(pkt));
struct arp_entry *entry, *free_entry = NULL, *non_pending = NULL;
struct net_buf *frag;
struct net_linkaddr *ll;
struct net_eth_hdr *hdr;
struct in_addr *addr;
@ -191,7 +207,11 @@ struct net_pkt *net_arp_prepare(struct net_pkt *pkt)
return NULL;
}
if (net_pkt_ll_reserve(pkt) != sizeof(struct net_eth_hdr)) {
if (net_pkt_ll_reserve(pkt) != sizeof(struct net_eth_hdr)
#if defined(CONFIG_NET_VLAN)
&& net_pkt_ll_reserve(pkt) != sizeof(struct net_eth_vlan_hdr)
#endif
) {
/* Add the ethernet header if it is missing. */
struct net_buf *header;
@ -200,24 +220,9 @@ struct net_pkt *net_arp_prepare(struct net_pkt *pkt)
return NULL;
}
net_pkt_set_ll_reserve(pkt, sizeof(struct net_eth_hdr));
hdr = (struct net_eth_hdr *)(header->data -
net_pkt_ll_reserve(pkt));
hdr->type = htons(NET_ETH_PTYPE_IP);
ll = net_pkt_ll_dst(pkt);
if (ll->addr) {
memcpy(&hdr->dst.addr, ll->addr,
sizeof(struct net_eth_addr));
}
ll = net_pkt_ll_src(pkt);
if (ll->addr) {
memcpy(&hdr->src.addr, ll->addr,
sizeof(struct net_eth_addr));
}
net_eth_fill_header(ctx, pkt, header, htons(NET_ETH_PTYPE_IP),
net_pkt_ll_src(pkt)->addr,
net_pkt_ll_dst(pkt)->addr);
net_pkt_frag_insert(pkt, header);
@ -293,14 +298,9 @@ struct net_pkt *net_arp_prepare(struct net_pkt *pkt)
continue;
}
hdr = (struct net_eth_hdr *)(frag->data -
net_pkt_ll_reserve(pkt));
hdr->type = htons(NET_ETH_PTYPE_IP);
memcpy(&hdr->src.addr, ll->addr,
sizeof(struct net_eth_addr));
memcpy(&hdr->dst.addr, &entry->eth.addr,
sizeof(struct net_eth_addr));
hdr = net_eth_fill_header(ctx, pkt, frag,
htons(NET_ETH_PTYPE_IP),
ll->addr, entry->eth.addr);
frag = frag->frags;
}
@ -368,13 +368,21 @@ static inline void arp_update(struct net_if *iface,
static inline struct net_pkt *prepare_arp_reply(struct net_if *iface,
struct net_pkt *req)
{
struct ethernet_context *ctx = net_if_l2_data(iface);
int eth_hdr_len = sizeof(struct net_eth_hdr);
struct net_pkt *pkt;
struct net_buf *frag;
struct net_arp_hdr *hdr, *query;
struct net_eth_hdr *eth, *eth_query;
pkt = net_pkt_get_reserve_tx(sizeof(struct net_eth_hdr),
NET_BUF_TIMEOUT);
#if defined(CONFIG_NET_VLAN)
if (ctx->vlan_enabled &&
net_eth_get_vlan_tag(iface) != NET_VLAN_TAG_UNSPEC) {
eth_hdr_len = sizeof(struct net_eth_vlan_hdr);
}
#endif
pkt = net_pkt_get_reserve_tx(eth_hdr_len, NET_BUF_TIMEOUT);
if (!pkt) {
goto fail;
}
@ -393,12 +401,13 @@ static inline struct net_pkt *prepare_arp_reply(struct net_if *iface,
query = NET_ARP_HDR(req);
eth_query = NET_ETH_HDR(req);
eth->type = htons(NET_ETH_PTYPE_ARP);
#if defined(CONFIG_NET_VLAN)
net_pkt_set_vlan_tag(pkt, net_pkt_vlan_tag(req));
#endif
memcpy(&eth->dst.addr, &eth_query->src.addr,
sizeof(struct net_eth_addr));
memcpy(&eth->src.addr, net_if_get_link_addr(iface)->addr,
sizeof(struct net_eth_addr));
net_eth_fill_header(ctx, pkt, frag, htons(NET_ETH_PTYPE_ARP),
net_if_get_link_addr(iface)->addr,
eth_query->src.addr);
hdr->hwtype = htons(NET_ARP_HTYPE_ETH);
hdr->protocol = htons(NET_ETH_PTYPE_IP);

View file

@ -46,21 +46,40 @@ void net_eth_ipv6_mcast_to_mac_addr(const struct in6_addr *ipv6_addr,
}
#if defined(CONFIG_NET_DEBUG_L2_ETHERNET)
#define print_ll_addrs(pkt, type, len) \
#define print_ll_addrs(pkt, type, len, src, dst) \
do { \
char out[sizeof("xx:xx:xx:xx:xx:xx")]; \
\
snprintk(out, sizeof(out), "%s", \
net_sprint_ll_addr(net_pkt_ll_src(pkt)->addr, \
net_sprint_ll_addr((src)->addr, \
sizeof(struct net_eth_addr))); \
\
NET_DBG("src %s dst %s type 0x%x len %zu", out, \
net_sprint_ll_addr(net_pkt_ll_dst(pkt)->addr, \
NET_DBG("iface %p src %s dst %s type 0x%x len %zu", \
net_pkt_iface(pkt), out, \
net_sprint_ll_addr((dst)->addr, \
sizeof(struct net_eth_addr)), \
type, (size_t)len); \
} while (0)
#define print_vlan_ll_addrs(pkt, type, tci, len, src, dst) \
do { \
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))); \
\
NET_DBG("iface %p src %s dst %s type 0x%x tag %d pri %d " \
"len %zu", \
net_pkt_iface(pkt), out, \
net_sprint_ll_addr((dst)->addr, \
sizeof(struct net_eth_addr)), \
type, net_eth_vlan_get_vid(tci), \
net_eth_vlan_get_pcp(tci), (size_t)len); \
} while (0)
#else
#define print_ll_addrs(...)
#define print_vlan_ll_addrs(...)
#endif /* CONFIG_NET_DEBUG_L2_ETHERNET */
static inline void ethernet_update_length(struct net_if *iface,
@ -100,11 +119,31 @@ static inline void ethernet_update_length(struct net_if *iface,
static enum net_verdict ethernet_recv(struct net_if *iface,
struct net_pkt *pkt)
{
#if defined(CONFIG_NET_VLAN)
struct net_eth_vlan_hdr *hdr_vlan =
(struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt);
struct ethernet_context *ctx = net_if_l2_data(iface);
bool vlan_enabled = false;
#endif
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
struct net_linkaddr *lladdr;
sa_family_t family;
u16_t type = ntohs(hdr->type);
u8_t hdr_len = sizeof(struct net_eth_hdr);
switch (ntohs(hdr->type)) {
#if defined(CONFIG_NET_VLAN)
if (net_eth_is_vlan_enabled(ctx, iface)) {
if (type == NET_ETH_PTYPE_VLAN) {
net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci));
type = ntohs(hdr_vlan->type);
hdr_len = sizeof(struct net_eth_vlan_hdr);
}
vlan_enabled = true;
}
#endif
switch (type) {
case NET_ETH_PTYPE_IP:
case NET_ETH_PTYPE_ARP:
net_pkt_set_family(pkt, AF_INET);
@ -115,7 +154,7 @@ static enum net_verdict ethernet_recv(struct net_if *iface,
family = AF_INET6;
break;
default:
NET_DBG("Unknown hdr type 0x%04x", hdr->type);
NET_DBG("Unknown hdr type 0x%04x", type);
return NET_DROP;
}
@ -130,7 +169,17 @@ static enum net_verdict ethernet_recv(struct net_if *iface,
lladdr->len = sizeof(struct net_eth_addr);
lladdr->type = NET_LINK_ETHERNET;
print_ll_addrs(pkt, ntohs(hdr->type), net_pkt_get_len(pkt));
#if defined(CONFIG_NET_VLAN)
if (vlan_enabled) {
print_vlan_ll_addrs(pkt, type, ntohs(hdr_vlan->vlan.tci),
net_pkt_get_len(pkt),
net_pkt_ll_src(pkt), net_pkt_ll_dst(pkt));
} else
#endif
{
print_ll_addrs(pkt, type, net_pkt_get_len(pkt),
net_pkt_ll_src(pkt), net_pkt_ll_dst(pkt));
}
if (!net_eth_is_addr_broadcast((struct net_eth_addr *)lladdr->addr) &&
!net_eth_is_addr_multicast((struct net_eth_addr *)lladdr->addr) &&
@ -145,11 +194,11 @@ static enum net_verdict ethernet_recv(struct net_if *iface,
return NET_DROP;
}
net_pkt_set_ll_reserve(pkt, sizeof(struct net_eth_hdr));
net_pkt_set_ll_reserve(pkt, hdr_len);
net_buf_pull(pkt->frags, net_pkt_ll_reserve(pkt));
#ifdef CONFIG_NET_ARP
if (family == AF_INET && hdr->type == htons(NET_ETH_PTYPE_ARP)) {
if (family == AF_INET && type == NET_ETH_PTYPE_ARP) {
NET_DBG("ARP packet from %s received",
net_sprint_ll_addr((u8_t *)hdr->src.addr,
sizeof(struct net_eth_addr)));
@ -196,10 +245,128 @@ static inline bool check_if_dst_is_broadcast_or_mcast(struct net_if *iface,
return false;
}
#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;
}
#endif /* CONFIG_NET_VLAN */
struct net_eth_hdr *net_eth_fill_header(struct ethernet_context *ctx,
struct net_pkt *pkt,
struct net_buf *frag,
u32_t ptype,
u8_t *src,
u8_t *dst)
{
struct net_eth_hdr *hdr;
NET_ASSERT(net_buf_headroom(frag) > sizeof(struct net_eth_addr));
#if defined(CONFIG_NET_VLAN)
if (net_eth_is_vlan_enabled(ctx, net_pkt_iface(pkt))) {
struct net_eth_vlan_hdr *hdr_vlan;
hdr_vlan = (struct net_eth_vlan_hdr *)(frag->data -
net_pkt_ll_reserve(pkt));
if (dst && ((u8_t *)&hdr_vlan->dst != dst)) {
memcpy(&hdr_vlan->dst, dst,
sizeof(struct net_eth_addr));
}
if (src && ((u8_t *)&hdr_vlan->src != src)) {
memcpy(&hdr_vlan->src, src,
sizeof(struct net_eth_addr));
}
hdr_vlan->type = ptype;
hdr_vlan->vlan.tpid = htons(NET_ETH_PTYPE_VLAN);
hdr_vlan->vlan.tci = htons(net_pkt_vlan_tci(pkt));
print_vlan_ll_addrs(pkt, ntohs(hdr_vlan->type),
net_pkt_vlan_tci(pkt),
frag->len,
&hdr_vlan->src, &hdr_vlan->dst);
return (struct net_eth_hdr *)hdr_vlan;
}
#endif
hdr = (struct net_eth_hdr *)(frag->data - net_pkt_ll_reserve(pkt));
if (dst && ((u8_t *)&hdr->dst != dst)) {
memcpy(&hdr->dst, dst, sizeof(struct net_eth_addr));
}
if (src && ((u8_t *)&hdr->src != src)) {
memcpy(&hdr->src, src, sizeof(struct net_eth_addr));
}
hdr->type = ptype;
print_ll_addrs(pkt, ntohs(hdr->type), frag->len, &hdr->src, &hdr->dst);
return hdr;
}
static enum net_verdict ethernet_send(struct net_if *iface,
struct net_pkt *pkt)
{
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
struct ethernet_context *ctx = net_if_l2_data(iface);
struct net_buf *frag;
u16_t ptype;
@ -305,6 +472,14 @@ setup_hdr:
ptype = htons(NET_ETH_PTYPE_IPV6);
}
#if defined(CONFIG_NET_VLAN)
if (net_eth_is_vlan_enabled(ctx, iface)) {
if (set_vlan_tag(ctx, iface, pkt) == NET_DROP) {
return NET_DROP;
}
}
#endif /* CONFIG_NET_VLAN */
/* Then go through the fragments and set the ethernet header.
*/
frag = pkt->frags;
@ -312,22 +487,9 @@ setup_hdr:
NET_ASSERT_INFO(frag, "No data!");
while (frag) {
NET_ASSERT(net_buf_headroom(frag) > sizeof(struct net_eth_addr));
hdr = (struct net_eth_hdr *)(frag->data -
net_pkt_ll_reserve(pkt));
if ((u8_t *)&hdr->dst != net_pkt_ll_dst(pkt)->addr) {
memcpy(&hdr->dst, net_pkt_ll_dst(pkt)->addr,
sizeof(struct net_eth_addr));
}
if ((u8_t *)&hdr->src != net_pkt_ll_src(pkt)->addr) {
memcpy(&hdr->src, net_pkt_ll_src(pkt)->addr,
sizeof(struct net_eth_addr));
}
hdr->type = ptype;
print_ll_addrs(pkt, ntohs(hdr->type), frag->len);
net_eth_fill_header(ctx, pkt, frag, ptype,
net_pkt_ll_src(pkt)->addr,
net_pkt_ll_dst(pkt)->addr);
frag = frag->frags;
}
@ -343,7 +505,14 @@ send:
static inline u16_t ethernet_reserve(struct net_if *iface, void *unused)
{
ARG_UNUSED(iface);
#if defined(CONFIG_NET_VLAN)
struct ethernet_context *ctx = net_if_l2_data(iface);
if (net_eth_is_vlan_enabled(ctx, iface)) {
return sizeof(struct net_eth_vlan_hdr);
}
#endif
ARG_UNUSED(unused);
return sizeof(struct net_eth_hdr);
@ -360,5 +529,241 @@ static inline int ethernet_enable(struct net_if *iface, bool state)
return 0;
}
#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);
int i;
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC ||
ctx->vlan[i].tag != tag) {
continue;
}
NET_DBG("[%d] vlan tag %d -> iface %p", i, tag,
ctx->vlan[i].iface);
return ctx->vlan[i].iface;
}
return NULL;
}
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;
}
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;
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) {
eth->vlan_setup(iface, tag, true);
}
ctx->vlan_enabled++;
if (ctx->vlan_enabled > NET_VLAN_MAX_COUNT) {
ctx->vlan_enabled = NET_VLAN_MAX_COUNT;
}
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;
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) {
eth->vlan_setup(iface, tag, false);
}
ctx->vlan_enabled--;
if (ctx->vlan_enabled < 0) {
ctx->vlan_enabled = 0;
}
return 0;
}
#endif
NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, ethernet_reserve,
ethernet_enable);
void ethernet_init(struct net_if *iface)
{
#if defined(CONFIG_NET_VLAN)
struct ethernet_context *ctx = net_if_l2_data(iface);
int i;
if (!(net_eth_get_hw_capabilities(iface) & ETH_HW_VLAN)) {
return;
}
NET_DBG("Initializing Ethernet L2 %p for iface %p", ctx, iface);
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;
}
}
ctx->is_init = true;
#else
ARG_UNUSED(iface);
#endif
}

View file

@ -2192,6 +2192,24 @@ void net_if_init(void)
#endif
}
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_VLAN)
/* Make sure that we do not have too many network interfaces
* compared to the number of VLAN interfaces.
*/
for (iface = __net_if_start, if_count = 0;
iface != __net_if_end; iface++) {
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
if_count++;
}
}
if (if_count > CONFIG_NET_VLAN_COUNT) {
NET_WARN("You have configured only %d VLAN interfaces"
" but you have %d network interfaces.",
CONFIG_NET_VLAN_COUNT, if_count);
}
#endif
}
void net_if_post_init(void)

View file

@ -322,6 +322,8 @@ struct net_pkt *net_pkt_get_reserve(struct k_mem_slab *slab,
net_pkt_set_priority(pkt, CONFIG_NET_TX_DEFAULT_PRIORITY);
#endif
net_pkt_set_vlan_tag(pkt, NET_VLAN_TAG_UNSPEC);
#if defined(CONFIG_NET_DEBUG_NET_PKT)
net_pkt_alloc_add(pkt, true, caller, line);
@ -2012,6 +2014,7 @@ struct net_pkt *net_pkt_clone(struct net_pkt *pkt, s32_t timeout)
net_pkt_set_next_hdr(clone, NULL);
net_pkt_set_ip_hdr_len(clone, net_pkt_ip_hdr_len(pkt));
net_pkt_set_vlan_tag(clone, net_pkt_vlan_tag(pkt));
net_pkt_set_family(clone, net_pkt_family(pkt));

View file

@ -47,6 +47,10 @@
#include <net/arp.h>
#endif
#if defined(CONFIG_NET_VLAN)
#include <net/ethernet.h>
#endif
#include "net_shell.h"
#include "net_stats.h"
@ -165,6 +169,9 @@ static void iface_cb(struct net_if *iface, void *user_data)
#endif
#if defined(CONFIG_NET_IPV4)
struct net_if_ipv4 *ipv4;
#endif
#if defined(CONFIG_NET_VLAN)
struct ethernet_context *eth_ctx;
#endif
struct net_if_addr *unicast;
struct net_if_mcast_addr *mcast;
@ -182,10 +189,37 @@ static void iface_cb(struct net_if *iface, void *user_data)
return;
}
printk("Link addr : %s\n",
net_sprint_ll_addr(net_if_get_link_addr(iface)->addr,
net_if_get_link_addr(iface)->len));
#if defined(CONFIG_NET_VLAN)
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
printk("Link addr : %s\n",
net_sprint_ll_addr(net_if_get_link_addr(iface)->addr,
net_if_get_link_addr(iface)->len));
printk("MTU : %d\n", net_if_get_mtu(iface));
eth_ctx = net_if_l2_data(iface);
if (eth_ctx->vlan_enabled) {
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
if (eth_ctx->vlan[i].iface != iface ||
eth_ctx->vlan[i].tag ==
NET_VLAN_TAG_UNSPEC) {
continue;
}
printk("[%d] VLAN tag : %d (0x%x)\n", i,
eth_ctx->vlan[i].tag,
eth_ctx->vlan[i].tag);
}
} else {
printk("VLAN not enabled\n");
}
}
#else
printk("Link addr : %s\n", net_sprint_ll_addr(
net_if_get_link_addr(iface)->addr,
net_if_get_link_addr(iface)->len));
printk("MTU : %d\n", net_if_get_mtu(iface));
#endif
#if defined(CONFIG_NET_IPV6)
count = 0;

View file

@ -24,6 +24,8 @@
#include <class/usb_cdc.h>
#include <net/ethernet.h>
#include <net/ethernet.h>
#include "../../usb_descriptor.h"
#include "../../composite.h"
#include "netusb.h"

View file

@ -229,3 +229,6 @@ CONFIG_HTTP_HEADER_FIELD_ITEMS=2
CONFIG_HTTP_CLIENT=y
CONFIG_HTTP_PARSER=y
CONFIG_HTTP_PARSER_STRICT=y
# VLAN
CONFIG_NET_VLAN=y

View file

@ -96,6 +96,8 @@ static void eth_iface_init(struct net_if *iface)
DBG("Iface %p addr %s\n", iface,
net_sprint_ll_addr(context->mac_addr, sizeof(context->mac_addr)));
ethernet_init(iface);
}
static int eth_tx_offloading_disabled(struct net_if *iface, struct net_pkt *pkt)
@ -250,15 +252,17 @@ static int eth_init(struct device *dev)
return 0;
}
NET_DEVICE_INIT(eth_offloading_disabled_test, "eth_offloading_disabled_test",
eth_init, &eth_context_offloading_disabled,
NULL, CONFIG_ETH_INIT_PRIORITY, &api_funcs_offloading_disabled,
ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), 1500);
ETH_NET_DEVICE_INIT(eth_offloading_disabled_test,
"eth_offloading_disabled_test",
eth_init, &eth_context_offloading_disabled,
NULL, CONFIG_ETH_INIT_PRIORITY,
&api_funcs_offloading_disabled, 1500);
NET_DEVICE_INIT(eth_offloading_enabled_test, "eth_offloading_enabled_test",
eth_init, &eth_context_offloading_enabled,
NULL, CONFIG_ETH_INIT_PRIORITY, &api_funcs_offloading_enabled,
ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), 1500);
ETH_NET_DEVICE_INIT(eth_offloading_enabled_test,
"eth_offloading_enabled_test",
eth_init, &eth_context_offloading_enabled,
NULL, CONFIG_ETH_INIT_PRIORITY,
&api_funcs_offloading_enabled, 1500);
struct user_data {
int eth_if_count;