// 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. Load more
// 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 MODULE static 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;