2831 lines · 74.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  linux/drivers/net/netconsole.c * *  Copyright (C) 2001  Ingo Molnar <mingo@redhat.com> * *  This file contains the implementation of an IRQ-safe, crash-safe *  kernel console implementation that outputs kernel messages to the *  network. * * Modification history: * * 2001-09-17    started by Ingo Molnar. * 2003-08-11    2.6 port by Matt Mackall *               simplified options *               generic card hooks *               works non-modular * 2003-09-07    rewritten with netpoll api */ /**************************************************************** * ****************************************************************/ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/mm.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/console.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ip.h>#include <linux/ipv6.h>#include <linux/udp.h>#include <linux/netpoll.h>#include <linux/inet.h>#include <linux/inetdevice.h>#include <linux/unaligned.h>#include <net/ip6_checksum.h>#include <net/addrconf.h>#include <linux/configfs.h>#include <linux/etherdevice.h>#include <linux/hex.h>#include <linux/u64_stats_sync.h>#include <linux/utsname.h>#include <linux/rtnetlink.h>#include <linux/workqueue.h>#include <linux/delay.h> MODULE_AUTHOR("Matt Mackall <mpm@selenic.com>");MODULE_DESCRIPTION("Console driver for network interfaces");MODULE_LICENSE("GPL");MODULE_IMPORT_NS("NETDEV_INTERNAL"); #define MAX_PARAM_LENGTH		256#define MAX_EXTRADATA_ENTRY_LEN		256#define MAX_EXTRADATA_VALUE_LEN	200/* The number 3 comes from userdata entry format characters (' ', '=', '\n') */#define MAX_EXTRADATA_NAME_LEN		(MAX_EXTRADATA_ENTRY_LEN - \					MAX_EXTRADATA_VALUE_LEN - 3)#define MAX_USERDATA_ITEMS		256#define MAX_PRINT_CHUNK			1000 /* * Sizing for the per-target fallback skb pool consulted by find_skb() * when its GFP_ATOMIC allocation fails so messages still get out under * memory pressure. */#define MAX_UDP_CHUNK			1460#define MAX_SKBS			32#define MAX_SKB_SIZE							\	(sizeof(struct ethhdr) +					\	 sizeof(struct iphdr) +						\	 sizeof(struct udphdr) +					\	 MAX_UDP_CHUNK) static char config[MAX_PARAM_LENGTH];module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0);MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]"); static bool oops_only;module_param(oops_only, bool, 0600);MODULE_PARM_DESC(oops_only, "Only log oops messages"); #define NETCONSOLE_PARAM_TARGET_PREFIX "cmdline" #ifndef	MODULEstatic int __init option_setup(char *opt){	strscpy(config, opt, MAX_PARAM_LENGTH);	return 1;}__setup("netconsole=", option_setup);#endif	/* MODULE */ /* Linked list of all configured targets */static LIST_HEAD(target_list);/* target_cleanup_list is used to track targets that need to be cleaned outside * of target_list_lock. It should be cleaned in the same function it is * populated. */static LIST_HEAD(target_cleanup_list); /* This needs to be a spinlock because write_msg() cannot sleep */static DEFINE_SPINLOCK(target_list_lock);/* This needs to be a mutex because netpoll_cleanup might sleep */static DEFINE_MUTEX(target_cleanup_list_lock); static struct workqueue_struct *netconsole_wq; /* * Console driver for netconsoles.  Register only consoles that have * an associated target of the same type. */static struct console netconsole_ext, netconsole; struct netconsole_target_stats  {	u64_stats_t xmit_drop_count;	u64_stats_t enomem_count;	struct u64_stats_sync syncp;}; enum console_type {	CONS_BASIC = BIT(0),	CONS_EXTENDED = BIT(1),}; /* Features enabled in sysdata. Contrary to userdata, this data is populated by * the kernel. The fields are designed as bitwise flags, allowing multiple * features to be set in sysdata_fields. */enum sysdata_feature {	/* Populate the CPU that sends the message */	SYSDATA_CPU_NR = BIT(0),	/* Populate the task name (as in current->comm) in sysdata */	SYSDATA_TASKNAME = BIT(1),	/* Kernel release/version as part of sysdata */	SYSDATA_RELEASE = BIT(2),	/* Include a per-target message ID as part of sysdata */	SYSDATA_MSGID = BIT(3),	/* Sentinel: highest bit position */	MAX_SYSDATA_ITEMS = 4,}; enum target_state {	STATE_DISABLED,	STATE_ENABLED,	STATE_DEACTIVATED,}; /** * struct netcons_userdata - Formatted userdata payload of a target. * @rcu:	Used to free the payload after a grace period. * @length:	Length of @data, excluding the NUL terminator. * @data:	Formatted " key=value\n" entries, NUL terminated. * * Immutable once published, so the transmit path never observes @data and * @length disagreeing. */struct netcons_userdata {	struct rcu_head		rcu;	size_t			length;	char			data[];}; /** * struct netconsole_target - Represents a configured netconsole target. * @list:	Links this target into the target_list. * @group:	Links us into the configfs subsystem hierarchy. * @userdata_group:	Links to the userdata configfs hierarchy * @userdata:		Cached, formatted userdata payload. RCU protected. * @sysdata:		Cached, formatted string of append * @sysdata_fields:	Sysdata features enabled. * @msgcounter:	Message sent counter. * @stats:	Packet send stats for the target. Used for debugging. * @state:	State of the target. *		Visible from userspace (read-write). *		From a userspace perspective, the target is either enabled or *		disabled. Internally, although both STATE_DISABLED and *		STATE_DEACTIVATED correspond to inactive targets, the latter is *		due to automatic interface state changes and will try *		recover automatically, if the interface comes back *		online. *		Also, other parameters of a target may be modified at *		runtime only when it is disabled (state != STATE_ENABLED). * @extended:	Denotes whether console is extended or not. * @release:	Denotes whether kernel release version should be prepended *		to the message. Depends on extended console. * @np:		The netpoll structure for this target. *		Contains the other userspace visible parameters: *		dev_name	(read-write) *		local_mac	(read-only) * @local_ip:	Source IP address of the target (read-write). * @remote_ip:	Destination IP address of the target (read-write). * @ipv6:	Whether the target addresses are IPv6 (read-write). * @local_port:	Source UDP port of the target (read-write). * @remote_port: Destination UDP port of the target (read-write). * @remote_mac:	Destination ethernet address of the target (read-write). * @buf:	The buffer used to send the full msg to the network stack * @resume_wq:	Workqueue to resume deactivated target * @skb_pool:	Per-target fallback skb pool consulted by find_skb() when *		its GFP_ATOMIC allocation fails. Lifetime brackets a *		successful netpoll_setup() / netpoll_cleanup() pair on @np. * @refill_wq:	Work item that asynchronously tops @skb_pool back up to *		MAX_SKBS after find_skb() drains an entry. */struct netconsole_target {	struct list_head	list;#ifdef	CONFIG_NETCONSOLE_DYNAMIC	struct config_group	group;	struct config_group	userdata_group;	struct netcons_userdata __rcu *userdata;	char			sysdata[MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS]; 	/* bit-wise with sysdata_feature bits */	u32			sysdata_fields;	/* protected by target_list_lock */	u32			msgcounter;#endif	struct netconsole_target_stats stats;	enum target_state	state;	bool			extended;	bool			release;	struct netpoll		np;	union inet_addr		local_ip, remote_ip;	bool			ipv6;	u16			local_port, remote_port;	u8			remote_mac[ETH_ALEN];	/* protected by target_list_lock; +1 gives scnprintf() room for its	 * NUL terminator so a full MAX_PRINT_CHUNK payload is not truncated	 */	char			buf[MAX_PRINT_CHUNK + 1];	struct work_struct	resume_wq;	struct sk_buff_head	skb_pool;	struct work_struct	refill_wq;}; #ifdef	CONFIG_NETCONSOLE_DYNAMIC static struct configfs_subsystem netconsole_subsys;static DEFINE_MUTEX(dynamic_netconsole_mutex); static int __init dynamic_netconsole_init(void){	config_group_init(&netconsole_subsys.su_group);	mutex_init(&netconsole_subsys.su_mutex);	return configfs_register_subsystem(&netconsole_subsys);} static void __exit dynamic_netconsole_exit(void){	configfs_unregister_subsystem(&netconsole_subsys);} /* * Targets that were created by parsing the boot/module option string * do not exist in the configfs hierarchy (and have NULL names) and will * never go away, so make these a no-op for them. */static void netconsole_target_get(struct netconsole_target *nt){	if (config_item_name(&nt->group.cg_item))		config_group_get(&nt->group);} static void netconsole_target_put(struct netconsole_target *nt){	if (config_item_name(&nt->group.cg_item))		config_group_put(&nt->group);} static void dynamic_netconsole_mutex_lock(void){	mutex_lock(&dynamic_netconsole_mutex);} static void dynamic_netconsole_mutex_unlock(void){	mutex_unlock(&dynamic_netconsole_mutex);} #else	/* !CONFIG_NETCONSOLE_DYNAMIC */ static int __init dynamic_netconsole_init(void){	return 0;} static void __exit dynamic_netconsole_exit(void){} /* * No danger of targets going away from under us when dynamic * reconfigurability is off. */static void netconsole_target_get(struct netconsole_target *nt){} static void netconsole_target_put(struct netconsole_target *nt){} static void populate_configfs_item(struct netconsole_target *nt,				   int cmdline_count){} static void dynamic_netconsole_mutex_lock(void){} static void dynamic_netconsole_mutex_unlock(void){} #endif	/* CONFIG_NETCONSOLE_DYNAMIC */ /* Check if the target was bound by mac address. */static bool bound_by_mac(struct netconsole_target *nt){	return is_valid_ether_addr(nt->np.dev_mac);} static void netcons_release_dev(struct netconsole_target *nt){	do_netpoll_cleanup(&nt->np);	if (bound_by_mac(nt))		memset(&nt->np.dev_name, 0, IFNAMSIZ);} static void refill_skbs(struct netconsole_target *nt){	struct sk_buff_head *skb_pool = &nt->skb_pool;	struct sk_buff *skb; 	while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);		if (!skb)			break; 		skb_queue_tail(skb_pool, skb);	}} static void refill_skbs_work_handler(struct work_struct *work){	struct netconsole_target *nt =		container_of(work, struct netconsole_target, refill_wq); 	refill_skbs(nt);} /* Seed the per-target skb pool that find_skb() falls back to. The queue * head and refill work are set up once in alloc_and_init(); this only * (re)fills the pool. Pair with netconsole_skb_pool_flush(). */static void netconsole_skb_pool_init(struct netconsole_target *nt){	refill_skbs(nt);} static void netconsole_skb_pool_flush(struct netconsole_target *nt){	cancel_work_sync(&nt->refill_wq);	skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED);} static void netcons_wait_carrier(struct netpoll *np, struct net_device *ndev){	unsigned long atmost; 	atmost = jiffies + netpoll_get_carrier_timeout() * HZ;	while (!netif_carrier_ok(ndev)) {		if (time_after(jiffies, atmost)) {			np_notice(np, "timeout waiting for carrier\n");			break;		}		msleep(1);	}} /* * Returns a pointer to a string representation of the identifier used * to select the egress interface for the given netpoll instance. buf * is used to format np->dev_mac when np->dev_name is empty; bufsz must * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address * and its NUL terminator. */static char *netcons_egress_dev(struct netpoll *np, char *buf, size_t bufsz){	if (np->dev_name[0])		return np->dev_name; 	snprintf(buf, bufsz, "%pM", np->dev_mac);	return buf;} /* * Populate the target's local_ip with the IPv6 address from ndev. */static int netcons_take_ipv6(struct netconsole_target *nt,			     struct net_device *ndev){	char buf[MAC_ADDR_STR_LEN + 1];	struct netpoll *np = &nt->np;	int err = -EDESTADDRREQ;	struct inet6_dev *idev; 	if (!IS_ENABLED(CONFIG_IPV6)) {		np_err(np, "IPv6 is not supported %s, aborting\n",		       netcons_egress_dev(np, buf, sizeof(buf)));		return -EINVAL;	} 	idev = __in6_dev_get(ndev);	if (idev) {		struct inet6_ifaddr *ifp; 		read_lock_bh(&idev->lock);		list_for_each_entry(ifp, &idev->addr_list, if_list) {			if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) !=				!!(ipv6_addr_type(&nt->remote_ip.in6) & IPV6_ADDR_LINKLOCAL))				continue;			/* Got the IP, let's return */			nt->local_ip.in6 = ifp->addr;			err = 0;			break;		}		read_unlock_bh(&idev->lock);	}	if (err) {		np_err(np, "no IPv6 address for %s, aborting\n",		       netcons_egress_dev(np, buf, sizeof(buf)));		return err;	} 	np_info(np, "local IPv6 %pI6c\n", &nt->local_ip.in6);	return 0;} /* * Populate the target's local_ip with the IPv4 address from ndev. */static int netcons_take_ipv4(struct netconsole_target *nt,			     struct net_device *ndev){	char buf[MAC_ADDR_STR_LEN + 1];	struct netpoll *np = &nt->np;	const struct in_ifaddr *ifa;	struct in_device *in_dev; 	in_dev = __in_dev_get_rtnl(ndev);	if (!in_dev) {		np_err(np, "no IP address for %s, aborting\n",		       netcons_egress_dev(np, buf, sizeof(buf)));		return -EDESTADDRREQ;	} 	ifa = rtnl_dereference(in_dev->ifa_list);	if (!ifa) {		np_err(np, "no IP address for %s, aborting\n",		       netcons_egress_dev(np, buf, sizeof(buf)));		return -EDESTADDRREQ;	} 	nt->local_ip.ip = ifa->ifa_local;	np_info(np, "local IP %pI4\n", &nt->local_ip.ip); 	return 0;} /* * Test whether the caller left nt->local_ip unset, so that * netcons_netpoll_setup() should auto-populate it from the egress device. * * nt->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — * doing so would misclassify a caller-supplied address as unset and * silently overwrite it with whatever address the device exposes. */static bool netcons_local_ip_unset(const struct netconsole_target *nt){	if (nt->ipv6)		return ipv6_addr_any(&nt->local_ip.in6);	return !nt->local_ip.ip;} static int netcons_netpoll_setup(struct netconsole_target *nt){	struct net *net = current->nsproxy->net_ns;	char buf[MAC_ADDR_STR_LEN + 1];	struct net_device *ndev = NULL;	struct netpoll *np = &nt->np;	bool ip_overwritten = false;

Lines 1–500 of 2831.