Merge branch 'netconsole-allow-selection-of-egress-interface-via-mac-address'

Uday Shankar says:

====================
netconsole: allow selection of egress interface via MAC address

This series adds support for selecting a netconsole egress interface by
specifying the MAC address (in place of the interface name) in the
boot/module parameter.

Signed-off-by: Uday Shankar <ushankar@purestorage.com>
====================

Link: https://patch.msgid.link/20250312-netconsole-v6-0-3437933e79b8@purestorage.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2025-03-19 19:17:59 +01:00
9 changed files with 61 additions and 22 deletions

View File

@@ -47,7 +47,7 @@ following format::
r if present, prepend kernel version (release) to the message
src-port source for UDP packets (defaults to 6665)
src-ip source IP to use (interface address)
dev network interface (eth0)
dev network interface name (eth0) or MAC address
tgt-port port for logging agent (6666)
tgt-ip IP address for logging agent
tgt-macaddr ethernet MAC address for logging agent (broadcast)
@@ -64,6 +64,10 @@ or using IPv6::
insmod netconsole netconsole=@/,@fd00:1:2:3::1/
or using a MAC address to select the egress interface::
linux netconsole=4444@10.0.0.1/22:33:44:55:66:77,9353@10.0.0.2/12:34:56:78:9a:bc
It also supports logging to multiple remote agents by specifying
parameters for the multiple agents separated by semicolons and the
complete string enclosed in "quotes", thusly::

View File

@@ -739,7 +739,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,
if (!mac_pton(buf, remote_mac))
goto out_unlock;
if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
if (buf[MAC_ADDR_STR_LEN] && buf[MAC_ADDR_STR_LEN] != '\n')
goto out_unlock;
memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);

View File

@@ -100,7 +100,7 @@ static int brcm_nvram_read_post_process_macaddr(void *context, const char *id, i
{
u8 mac[ETH_ALEN];
if (bytes != 3 * ETH_ALEN - 1)
if (bytes != MAC_ADDR_STR_LEN)
return -EINVAL;
if (!mac_pton(buf, mac))

View File

@@ -37,7 +37,7 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i
{
u8 mac[ETH_ALEN];
if (bytes != 3 * ETH_ALEN - 1)
if (bytes != MAC_ADDR_STR_LEN)
return -EINVAL;
if (!mac_pton(buf, mac))

View File

@@ -19,6 +19,9 @@
#include <linux/skbuff.h>
#include <uapi/linux/if_ether.h>
/* XX:XX:XX:XX:XX:XX */
#define MAC_ADDR_STR_LEN (3 * ETH_ALEN - 1)
static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
{
return (struct ethhdr *)skb_mac_header(skb);

View File

@@ -25,7 +25,13 @@ union inet_addr {
struct netpoll {
struct net_device *dev;
netdevice_tracker dev_tracker;
/*
* Either dev_name or dev_mac can be used to specify the local
* interface - dev_name is used if it is a nonempty string, else
* dev_mac is used.
*/
char dev_name[IFNAMSIZ];
u8 dev_mac[ETH_ALEN];
const char *name;
union inet_addr local_ip, remote_ip;

View File

@@ -7,11 +7,9 @@
bool mac_pton(const char *s, u8 *mac)
{
size_t maxlen = 3 * ETH_ALEN - 1;
int i;
/* XX:XX:XX:XX:XX:XX */
if (strnlen(s, maxlen) < maxlen)
if (strnlen(s, MAC_ADDR_STR_LEN) < MAC_ADDR_STR_LEN)
return false;
/* Don't dirty result unless string is valid MAC. */

View File

@@ -507,7 +507,8 @@ void netpoll_print_options(struct netpoll *np)
np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6);
else
np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip);
np_info(np, "interface '%s'\n", np->dev_name);
np_info(np, "interface name '%s'\n", np->dev_name);
np_info(np, "local ethernet address '%pM'\n", np->dev_mac);
np_info(np, "remote port %d\n", np->remote_port);
if (np->ipv6)
np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6);
@@ -577,11 +578,18 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
cur++;
if (*cur != ',') {
/* parse out dev name */
/* parse out dev_name or dev_mac */
if ((delim = strchr(cur, ',')) == NULL)
goto parse_failed;
*delim = 0;
strscpy(np->dev_name, cur, sizeof(np->dev_name));
np->dev_name[0] = '\0';
eth_broadcast_addr(np->dev_mac);
if (!strchr(cur, ':'))
strscpy(np->dev_name, cur, sizeof(np->dev_name));
else if (!mac_pton(cur, np->dev_mac))
goto parse_failed;
cur = delim;
}
cur++;
@@ -695,27 +703,45 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
}
EXPORT_SYMBOL_GPL(__netpoll_setup);
/*
* Returns a pointer to a string representation of the identifier used
* to select the egress interface for the given netpoll instance. buf
* must be a buffer of length at least MAC_ADDR_STR_LEN + 1.
*/
static char *egress_dev(struct netpoll *np, char *buf)
{
if (np->dev_name[0])
return np->dev_name;
snprintf(buf, MAC_ADDR_STR_LEN, "%pM", np->dev_mac);
return buf;
}
int netpoll_setup(struct netpoll *np)
{
struct net *net = current->nsproxy->net_ns;
char buf[MAC_ADDR_STR_LEN + 1];
struct net_device *ndev = NULL;
bool ip_overwritten = false;
struct in_device *in_dev;
int err;
rtnl_lock();
if (np->dev_name[0]) {
struct net *net = current->nsproxy->net_ns;
if (np->dev_name[0])
ndev = __dev_get_by_name(net, np->dev_name);
}
else if (is_valid_ether_addr(np->dev_mac))
ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac);
if (!ndev) {
np_err(np, "%s doesn't exist, aborting\n", np->dev_name);
np_err(np, "%s doesn't exist, aborting\n", egress_dev(np, buf));
err = -ENODEV;
goto unlock;
}
netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL);
if (netdev_master_upper_dev_get(ndev)) {
np_err(np, "%s is a slave device, aborting\n", np->dev_name);
np_err(np, "%s is a slave device, aborting\n",
egress_dev(np, buf));
err = -EBUSY;
goto put;
}
@@ -723,7 +749,8 @@ int netpoll_setup(struct netpoll *np)
if (!netif_running(ndev)) {
unsigned long atmost;
np_info(np, "device %s not up yet, forcing it\n", np->dev_name);
np_info(np, "device %s not up yet, forcing it\n",
egress_dev(np, buf));
err = dev_open(ndev, NULL);
@@ -757,7 +784,7 @@ int netpoll_setup(struct netpoll *np)
if (!ifa) {
put_noaddr:
np_err(np, "no IP address for %s, aborting\n",
np->dev_name);
egress_dev(np, buf));
err = -EDESTADDRREQ;
goto put;
}
@@ -788,13 +815,13 @@ int netpoll_setup(struct netpoll *np)
}
if (err) {
np_err(np, "no IPv6 address for %s, aborting\n",
np->dev_name);
egress_dev(np, buf));
goto put;
} else
np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6);
#else
np_err(np, "IPv6 is not supported %s, aborting\n",
np->dev_name);
egress_dev(np, buf));
err = -EINVAL;
goto put;
#endif

View File

@@ -457,11 +457,12 @@ static ssize_t link_sta_addr_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct link_sta_info *link_sta = file->private_data;
u8 mac[3 * ETH_ALEN + 1];
u8 mac[MAC_ADDR_STR_LEN + 2];
snprintf(mac, sizeof(mac), "%pM\n", link_sta->pub->addr);
return simple_read_from_buffer(userbuf, count, ppos, mac, 3 * ETH_ALEN);
return simple_read_from_buffer(userbuf, count, ppos, mac,
MAC_ADDR_STR_LEN + 1);
}
LINK_STA_OPS(addr);
@@ -1240,7 +1241,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
u8 mac[3*ETH_ALEN];
u8 mac[MAC_ADDR_STR_LEN + 1];
if (!stations_dir)
return;