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:
Jukka Rissanen 2024-04-11 18:14:38 +03:00 committed by Benjamin Cabé
parent 45ac1f9848
commit 420c4b153e
4 changed files with 133 additions and 20 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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)
{