Skip to content

Commit

Permalink
net: ethernet/vlan: Add support for embedding ll header
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
jukkar authored and kartben committed Dec 19, 2024
1 parent 45ac1f9 commit 420c4b1
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 20 deletions.
8 changes: 8 additions & 0 deletions subsys/net/l2/ethernet/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
113 changes: 94 additions & 19 deletions subsys/net/l2/ethernet/ethernet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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));
}

hdr_frag = net_pkt_get_frag(pkt, hdr_len, NET_BUF_TIMEOUT);
if (!hdr_frag) {
return NULL;
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_ENABLED(CONFIG_NET_VLAN) &&
net_eth_is_vlan_enabled(ctx, iface) &&
net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC) {
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) ||
Expand All @@ -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))) {
Expand All @@ -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;
}
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down
16 changes: 16 additions & 0 deletions subsys/net/l2/ethernet/vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
16 changes: 15 additions & 1 deletion subsys/net/l2/virtual/virtual.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit 420c4b1

Please sign in to comment.