Skip to content

Commit

Permalink
netconsole: allow selection of egress interface via MAC address
Browse files Browse the repository at this point in the history
Currently, netconsole has two methods of configuration - module
parameter and configfs. The former interface allows for netconsole
activation earlier during boot (by specifying the module parameter on
the kernel command line), so it is preferred for debugging issues which
arise before userspace is up/the configfs interface can be used. The
module parameter syntax requires specifying the egress interface name.
This requirement makes it hard to use for a couple reasons:
- The egress interface name can be hard or impossible to predict. For
  example, installing a new network card in a system can change the
  interface names assigned by the kernel.
- When constructing the module parameter, one may have trouble
  determining the original (kernel-assigned) name of the interface
  (which is the name that should be given to netconsole) if some stable
  interface naming scheme is in effect. A human can usually look at
  kernel logs to determine the original name, but this is very painful
  if automation is constructing the parameter.

For these reasons, allow selection of the egress interface via MAC
address when configuring netconsole using the module parameter. Update
the netconsole documentation with an example of the new syntax.
Selection of egress interface by MAC address via configfs is far less
interesting (since when this interface can be used, one should be able
to easily convert between MAC address and interface name), so it is left
unimplemented.

Signed-off-by: Uday Shankar <ushankar@purestorage.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
Tested-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250312-netconsole-v6-2-3437933e79b8@purestorage.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Uday Shankar authored and Paolo Abeni committed Mar 19, 2025
1 parent 6d6c1ba commit f8a10be
Showing 3 changed files with 50 additions and 13 deletions.
6 changes: 5 additions & 1 deletion Documentation/networking/netconsole.rst
Original file line number Diff line number Diff line change
@@ -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::
6 changes: 6 additions & 0 deletions include/linux/netpoll.h
Original file line number Diff line number Diff line change
@@ -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;
51 changes: 39 additions & 12 deletions net/core/netpoll.c
Original file line number Diff line number Diff line change
@@ -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,35 +703,54 @@ 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;
}

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

0 comments on commit f8a10be

Please sign in to comment.