[PATCH] netpoll: support sending over raw IP interfaces

From: Mark Cilissen
Date: Wed Mar 13 2024 - 08:46:41 EST


Currently, netpoll only supports interfaces with an ethernet-compatible
link layer. Certain interfaces like SLIP do not have a link layer
on the network interface level at all and expect raw IP packets,
and could benefit from being supported by netpoll.

This commit adds support for such interfaces by using the network device's
`hard_header_len` field as an indication that no link layer is present.
If that is the case we simply skip adding the ethernet header, causing
a raw IP packet to be sent over the interface. This has been confirmed
to add netconsole support to at least SLIP and WireGuard interfaces.

Signed-off-by: Mark Cilissen <mark@xxxxxxxxxx>
---
Documentation/networking/netconsole.rst | 3 ++-
net/core/netpoll.c | 30 ++++++++++++++++---------
2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index d55c2a22ec7a..434ce0366027 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -327,4 +327,5 @@ enable the logging of even the most critical kernel bugs. It works
from IRQ contexts as well, and does not enable interrupts while
sending packets. Due to these unique needs, configuration cannot
be more automatic, and some fundamental limitations will remain:
-only IP networks, UDP packets and ethernet devices are supported.
+only UDP packets over IP networks, over ethernet links if a
+hardware layer is required, are supported.
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 543007f159f9..0299fb71b456 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -399,7 +399,7 @@ EXPORT_SYMBOL(netpoll_send_skb);

void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
{
- int total_len, ip_len, udp_len;
+ int total_len, ip_len, udp_len, link_len;
struct sk_buff *skb;
struct udphdr *udph;
struct iphdr *iph;
@@ -416,7 +416,10 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
else
ip_len = udp_len + sizeof(*iph);

- total_len = ip_len + LL_RESERVED_SPACE(np->dev);
+ /* if there's a hardware header assume ethernet, else raw IP */
+ eth = NULL;
+ link_len = np->dev->hard_header_len ? LL_RESERVED_SPACE(np->dev) : 0;
+ total_len = ip_len + link_len;

skb = find_skb(np, total_len + np->dev->needed_tailroom,
total_len - len);
@@ -458,9 +461,11 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
ip6h->saddr = np->local_ip.in6;
ip6h->daddr = np->remote_ip.in6;

- eth = skb_push(skb, ETH_HLEN);
- skb_reset_mac_header(skb);
- skb->protocol = eth->h_proto = htons(ETH_P_IPV6);
+ skb->protocol = htons(ETH_P_IPV6);
+ if (link_len) {
+ eth = skb_push(skb, ETH_HLEN);
+ skb_reset_mac_header(skb);
+ }
} else {
udph->check = 0;
udph->check = csum_tcpudp_magic(np->local_ip.ip,
@@ -487,13 +492,18 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
put_unaligned(np->remote_ip.ip, &(iph->daddr));
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);

- eth = skb_push(skb, ETH_HLEN);
- skb_reset_mac_header(skb);
- skb->protocol = eth->h_proto = htons(ETH_P_IP);
+ skb->protocol = htons(ETH_P_IP);
+ if (link_len) {
+ eth = skb_push(skb, ETH_HLEN);
+ skb_reset_mac_header(skb);
+ }
}

- ether_addr_copy(eth->h_source, np->dev->dev_addr);
- ether_addr_copy(eth->h_dest, np->remote_mac);
+ if (eth) {
+ eth->h_proto = skb->protocol;
+ ether_addr_copy(eth->h_source, np->dev->dev_addr);
+ ether_addr_copy(eth->h_dest, np->remote_mac);
+ }

skb->dev = np->dev;

--
2.43.0