net: ethernet/vlan: Add support for embedding ll header
If Ethernet based network device driver is advertizing ETHERNET_EMBEDDED_LL_HEADER, then the L2 driver will can return the proper L2 header size for the network interface. This info is then used in TX to determine whether to have a separate net_buf for the link level header, or to embed the header to the same net_buf as the L2 payload. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
45ac1f9848
commit
420c4b153e
4 changed files with 133 additions and 20 deletions
|
|
@ -16,6 +16,14 @@ module-str = Log level for Ethernet L2 layer
|
|||
module-help = Enables Ethernet L2 to output debug messages.
|
||||
source "subsys/net/Kconfig.template.log_config.net"
|
||||
|
||||
config NET_L2_ETHERNET_RESERVE_HEADER
|
||||
bool "Reserve space for Ethernet header in first net_buf in TX"
|
||||
help
|
||||
If enabled, then reserve space for Ethernet header to the first
|
||||
net_buf when sending data. The default is still to have layer 2
|
||||
header in a separate net_buf. In RX side the Ethernet header
|
||||
is always part of the first net_buf.
|
||||
|
||||
config NET_L2_ETHERNET_MGMT
|
||||
bool "Ethernet network management interface"
|
||||
select NET_MGMT
|
||||
|
|
|
|||
|
|
@ -518,27 +518,79 @@ static bool ethernet_fill_in_dst_on_ipv6_mcast(struct net_pkt *pkt,
|
|||
#define ethernet_fill_in_dst_on_ipv6_mcast(...) false
|
||||
#endif /* CONFIG_NET_IPV6 */
|
||||
|
||||
static inline size_t get_reserve_ll_header_size(struct net_if *iface)
|
||||
{
|
||||
bool is_vlan = false;
|
||||
|
||||
#if defined(CONFIG_NET_VLAN)
|
||||
if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
|
||||
iface = net_eth_get_vlan_main(iface);
|
||||
is_vlan = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER)) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (is_vlan) {
|
||||
return sizeof(struct net_eth_vlan_hdr);
|
||||
} else {
|
||||
return sizeof(struct net_eth_hdr);
|
||||
}
|
||||
}
|
||||
|
||||
static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx,
|
||||
struct net_if *iface,
|
||||
struct net_pkt *pkt,
|
||||
uint32_t ptype)
|
||||
{
|
||||
struct net_if *orig_iface = iface;
|
||||
struct net_buf *hdr_frag;
|
||||
struct net_eth_hdr *hdr;
|
||||
size_t hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ?
|
||||
sizeof(struct net_eth_vlan_hdr) :
|
||||
sizeof(struct net_eth_hdr);
|
||||
size_t reserve_ll_header;
|
||||
size_t hdr_len;
|
||||
bool is_vlan;
|
||||
|
||||
hdr_frag = net_pkt_get_frag(pkt, hdr_len, NET_BUF_TIMEOUT);
|
||||
if (!hdr_frag) {
|
||||
return NULL;
|
||||
is_vlan = IS_ENABLED(CONFIG_NET_VLAN) &&
|
||||
net_eth_is_vlan_enabled(ctx, iface) &&
|
||||
net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC;
|
||||
if (is_vlan) {
|
||||
orig_iface = net_eth_get_vlan_iface(iface, net_pkt_vlan_tag(pkt));
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_NET_VLAN) &&
|
||||
net_eth_is_vlan_enabled(ctx, iface) &&
|
||||
net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC) {
|
||||
reserve_ll_header = get_reserve_ll_header_size(orig_iface);
|
||||
if (reserve_ll_header > 0) {
|
||||
hdr_len = reserve_ll_header;
|
||||
hdr_frag = pkt->buffer;
|
||||
|
||||
NET_DBG("Making room for link header %zd bytes", hdr_len);
|
||||
|
||||
/* Make room for the header */
|
||||
net_buf_push(pkt->buffer, hdr_len);
|
||||
} else {
|
||||
hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ?
|
||||
sizeof(struct net_eth_vlan_hdr) :
|
||||
sizeof(struct net_eth_hdr);
|
||||
|
||||
hdr_frag = net_pkt_get_frag(pkt, hdr_len, NET_BUF_TIMEOUT);
|
||||
if (!hdr_frag) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_vlan) {
|
||||
struct net_eth_vlan_hdr *hdr_vlan;
|
||||
|
||||
if (reserve_ll_header == 0U) {
|
||||
hdr_len = sizeof(struct net_eth_vlan_hdr);
|
||||
net_buf_add(hdr_frag, hdr_len);
|
||||
}
|
||||
|
||||
hdr_vlan = (struct net_eth_vlan_hdr *)(hdr_frag->data);
|
||||
|
||||
if (ptype == htons(NET_ETH_PTYPE_ARP) ||
|
||||
|
|
@ -554,15 +606,19 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx,
|
|||
hdr_vlan->type = ptype;
|
||||
hdr_vlan->vlan.tpid = htons(NET_ETH_PTYPE_VLAN);
|
||||
hdr_vlan->vlan.tci = htons(net_pkt_vlan_tci(pkt));
|
||||
net_buf_add(hdr_frag, sizeof(struct net_eth_vlan_hdr));
|
||||
|
||||
print_vlan_ll_addrs(pkt, ntohs(hdr_vlan->type),
|
||||
net_pkt_vlan_tci(pkt),
|
||||
hdr_frag->len,
|
||||
hdr_len,
|
||||
&hdr_vlan->src, &hdr_vlan->dst, false);
|
||||
} else {
|
||||
hdr = (struct net_eth_hdr *)(hdr_frag->data);
|
||||
|
||||
if (reserve_ll_header == 0U) {
|
||||
hdr_len = sizeof(struct net_eth_hdr);
|
||||
net_buf_add(hdr_frag, hdr_len);
|
||||
}
|
||||
|
||||
if (ptype == htons(NET_ETH_PTYPE_ARP) ||
|
||||
(!ethernet_fill_in_dst_on_ipv4_mcast(pkt, &hdr->dst) &&
|
||||
!ethernet_fill_in_dst_on_ipv6_mcast(pkt, &hdr->dst))) {
|
||||
|
|
@ -574,13 +630,14 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx,
|
|||
sizeof(struct net_eth_addr));
|
||||
|
||||
hdr->type = ptype;
|
||||
net_buf_add(hdr_frag, sizeof(struct net_eth_hdr));
|
||||
|
||||
print_ll_addrs(pkt, ntohs(hdr->type),
|
||||
hdr_frag->len, &hdr->src, &hdr->dst);
|
||||
hdr_len, &hdr->src, &hdr->dst);
|
||||
}
|
||||
|
||||
net_pkt_frag_insert(pkt, hdr_frag);
|
||||
if (reserve_ll_header == 0U) {
|
||||
net_pkt_frag_insert(pkt, hdr_frag);
|
||||
}
|
||||
|
||||
return hdr_frag;
|
||||
}
|
||||
|
|
@ -605,14 +662,19 @@ static void ethernet_update_tx_stats(struct net_if *iface, struct net_pkt *pkt)
|
|||
|
||||
static void ethernet_remove_l2_header(struct net_pkt *pkt)
|
||||
{
|
||||
size_t reserve = get_reserve_ll_header_size(net_pkt_iface(pkt));
|
||||
struct net_buf *buf;
|
||||
|
||||
/* Remove the buffer added in ethernet_fill_header() */
|
||||
buf = pkt->buffer;
|
||||
pkt->buffer = buf->frags;
|
||||
buf->frags = NULL;
|
||||
if (reserve == 0U) {
|
||||
buf = pkt->buffer;
|
||||
pkt->buffer = buf->frags;
|
||||
buf->frags = NULL;
|
||||
|
||||
net_pkt_frag_unref(buf);
|
||||
net_pkt_frag_unref(buf);
|
||||
} else {
|
||||
net_buf_pull(pkt->buffer, reserve);
|
||||
}
|
||||
}
|
||||
|
||||
static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
|
||||
|
|
@ -813,8 +875,21 @@ enum net_l2_flags ethernet_flags(struct net_if *iface)
|
|||
return ctx->ethernet_l2_flags;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER)
|
||||
static int ethernet_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
|
||||
size_t size, enum net_ip_protocol proto,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
return net_pkt_alloc_buffer_with_reserve(pkt, size,
|
||||
get_reserve_ll_header_size(iface),
|
||||
proto, timeout);
|
||||
}
|
||||
#else
|
||||
#define ethernet_l2_alloc NULL
|
||||
#endif
|
||||
|
||||
NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, ethernet_enable,
|
||||
ethernet_flags);
|
||||
ethernet_flags, ethernet_l2_alloc);
|
||||
|
||||
static void carrier_on_off(struct k_work *work)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -610,6 +610,22 @@ static enum net_verdict vlan_interface_recv(struct net_if *iface,
|
|||
return NET_OK;
|
||||
}
|
||||
|
||||
int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
|
||||
size_t size, uint16_t proto, k_timeout_t timeout)
|
||||
{
|
||||
enum virtual_interface_caps caps;
|
||||
int ret = 0;
|
||||
|
||||
caps = net_virtual_get_iface_capabilities(iface);
|
||||
if (caps & VIRTUAL_INTERFACE_VLAN) {
|
||||
ret = net_pkt_alloc_buffer_with_reserve(pkt, size,
|
||||
sizeof(struct net_eth_vlan_hdr),
|
||||
proto, timeout);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vlan_interface_attach(struct net_if *vlan_iface,
|
||||
struct net_if *iface)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -165,8 +165,22 @@ enum net_l2_flags virtual_flags(struct net_if *iface)
|
|||
return ctx->virtual_l2_flags;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN)
|
||||
extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
|
||||
size_t size, uint16_t proto, k_timeout_t timeout);
|
||||
|
||||
static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
|
||||
size_t size, enum net_ip_protocol proto,
|
||||
k_timeout_t timeout)
|
||||
{
|
||||
return vlan_alloc_buffer(iface, pkt, size, proto, timeout);
|
||||
}
|
||||
#else
|
||||
#define virtual_l2_alloc NULL
|
||||
#endif
|
||||
|
||||
NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable,
|
||||
virtual_flags);
|
||||
virtual_flags, virtual_l2_alloc);
|
||||
|
||||
static void random_linkaddr(uint8_t *linkaddr, size_t len)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue