LCOV - code coverage report
Current view: top level - drivers/net - netconsole.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 33 167 19.8 %
Date: 2022-03-28 16:04:14 Functions: 3 9 33.3 %
Branches: 8 85 9.4 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  linux/drivers/net/netconsole.c
       4                 :            :  *
       5                 :            :  *  Copyright (C) 2001  Ingo Molnar <mingo@redhat.com>
       6                 :            :  *
       7                 :            :  *  This file contains the implementation of an IRQ-safe, crash-safe
       8                 :            :  *  kernel console implementation that outputs kernel messages to the
       9                 :            :  *  network.
      10                 :            :  *
      11                 :            :  * Modification history:
      12                 :            :  *
      13                 :            :  * 2001-09-17    started by Ingo Molnar.
      14                 :            :  * 2003-08-11    2.6 port by Matt Mackall
      15                 :            :  *               simplified options
      16                 :            :  *               generic card hooks
      17                 :            :  *               works non-modular
      18                 :            :  * 2003-09-07    rewritten with netpoll api
      19                 :            :  */
      20                 :            : 
      21                 :            : /****************************************************************
      22                 :            :  *
      23                 :            :  ****************************************************************/
      24                 :            : 
      25                 :            : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      26                 :            : 
      27                 :            : #include <linux/mm.h>
      28                 :            : #include <linux/init.h>
      29                 :            : #include <linux/module.h>
      30                 :            : #include <linux/slab.h>
      31                 :            : #include <linux/console.h>
      32                 :            : #include <linux/moduleparam.h>
      33                 :            : #include <linux/kernel.h>
      34                 :            : #include <linux/string.h>
      35                 :            : #include <linux/netpoll.h>
      36                 :            : #include <linux/inet.h>
      37                 :            : #include <linux/configfs.h>
      38                 :            : #include <linux/etherdevice.h>
      39                 :            : 
      40                 :            : MODULE_AUTHOR("Maintainer: Matt Mackall <mpm@selenic.com>");
      41                 :            : MODULE_DESCRIPTION("Console driver for network interfaces");
      42                 :            : MODULE_LICENSE("GPL");
      43                 :            : 
      44                 :            : #define MAX_PARAM_LENGTH        256
      45                 :            : #define MAX_PRINT_CHUNK         1000
      46                 :            : 
      47                 :            : static char config[MAX_PARAM_LENGTH];
      48                 :            : module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0);
      49                 :            : MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]");
      50                 :            : 
      51                 :            : static bool oops_only = false;
      52                 :            : module_param(oops_only, bool, 0600);
      53                 :            : MODULE_PARM_DESC(oops_only, "Only log oops messages");
      54                 :            : 
      55                 :            : #ifndef MODULE
      56                 :          0 : static int __init option_setup(char *opt)
      57                 :            : {
      58                 :          0 :         strlcpy(config, opt, MAX_PARAM_LENGTH);
      59                 :          0 :         return 1;
      60                 :            : }
      61                 :            : __setup("netconsole=", option_setup);
      62                 :            : #endif  /* MODULE */
      63                 :            : 
      64                 :            : /* Linked list of all configured targets */
      65                 :            : static LIST_HEAD(target_list);
      66                 :            : 
      67                 :            : /* This needs to be a spinlock because write_msg() cannot sleep */
      68                 :            : static DEFINE_SPINLOCK(target_list_lock);
      69                 :            : 
      70                 :            : /*
      71                 :            :  * Console driver for extended netconsoles.  Registered on the first use to
      72                 :            :  * avoid unnecessarily enabling ext message formatting.
      73                 :            :  */
      74                 :            : static struct console netconsole_ext;
      75                 :            : 
      76                 :            : /**
      77                 :            :  * struct netconsole_target - Represents a configured netconsole target.
      78                 :            :  * @list:       Links this target into the target_list.
      79                 :            :  * @item:       Links us into the configfs subsystem hierarchy.
      80                 :            :  * @enabled:    On / off knob to enable / disable target.
      81                 :            :  *              Visible from userspace (read-write).
      82                 :            :  *              We maintain a strict 1:1 correspondence between this and
      83                 :            :  *              whether the corresponding netpoll is active or inactive.
      84                 :            :  *              Also, other parameters of a target may be modified at
      85                 :            :  *              runtime only when it is disabled (enabled == 0).
      86                 :            :  * @np:         The netpoll structure for this target.
      87                 :            :  *              Contains the other userspace visible parameters:
      88                 :            :  *              dev_name        (read-write)
      89                 :            :  *              local_port      (read-write)
      90                 :            :  *              remote_port     (read-write)
      91                 :            :  *              local_ip        (read-write)
      92                 :            :  *              remote_ip       (read-write)
      93                 :            :  *              local_mac       (read-only)
      94                 :            :  *              remote_mac      (read-write)
      95                 :            :  */
      96                 :            : struct netconsole_target {
      97                 :            :         struct list_head        list;
      98                 :            : #ifdef  CONFIG_NETCONSOLE_DYNAMIC
      99                 :            :         struct config_item      item;
     100                 :            : #endif
     101                 :            :         bool                    enabled;
     102                 :            :         bool                    extended;
     103                 :            :         struct netpoll          np;
     104                 :            : };
     105                 :            : 
     106                 :            : #ifdef  CONFIG_NETCONSOLE_DYNAMIC
     107                 :            : 
     108                 :            : static struct configfs_subsystem netconsole_subsys;
     109                 :            : static DEFINE_MUTEX(dynamic_netconsole_mutex);
     110                 :            : 
     111                 :            : static int __init dynamic_netconsole_init(void)
     112                 :            : {
     113                 :            :         config_group_init(&netconsole_subsys.su_group);
     114                 :            :         mutex_init(&netconsole_subsys.su_mutex);
     115                 :            :         return configfs_register_subsystem(&netconsole_subsys);
     116                 :            : }
     117                 :            : 
     118                 :            : static void __exit dynamic_netconsole_exit(void)
     119                 :            : {
     120                 :            :         configfs_unregister_subsystem(&netconsole_subsys);
     121                 :            : }
     122                 :            : 
     123                 :            : /*
     124                 :            :  * Targets that were created by parsing the boot/module option string
     125                 :            :  * do not exist in the configfs hierarchy (and have NULL names) and will
     126                 :            :  * never go away, so make these a no-op for them.
     127                 :            :  */
     128                 :            : static void netconsole_target_get(struct netconsole_target *nt)
     129                 :            : {
     130                 :            :         if (config_item_name(&nt->item))
     131                 :            :                 config_item_get(&nt->item);
     132                 :            : }
     133                 :            : 
     134                 :            : static void netconsole_target_put(struct netconsole_target *nt)
     135                 :            : {
     136                 :            :         if (config_item_name(&nt->item))
     137                 :            :                 config_item_put(&nt->item);
     138                 :            : }
     139                 :            : 
     140                 :            : #else   /* !CONFIG_NETCONSOLE_DYNAMIC */
     141                 :            : 
     142                 :         13 : static int __init dynamic_netconsole_init(void)
     143                 :            : {
     144                 :         13 :         return 0;
     145                 :            : }
     146                 :            : 
     147                 :          0 : static void __exit dynamic_netconsole_exit(void)
     148                 :            : {
     149                 :          0 : }
     150                 :            : 
     151                 :            : /*
     152                 :            :  * No danger of targets going away from under us when dynamic
     153                 :            :  * reconfigurability is off.
     154                 :            :  */
     155                 :          0 : static void netconsole_target_get(struct netconsole_target *nt)
     156                 :            : {
     157                 :          0 : }
     158                 :            : 
     159                 :          0 : static void netconsole_target_put(struct netconsole_target *nt)
     160                 :            : {
     161                 :          0 : }
     162                 :            : 
     163                 :            : #endif  /* CONFIG_NETCONSOLE_DYNAMIC */
     164                 :            : 
     165                 :            : /* Allocate new target (from boot/module param) and setup netpoll for it */
     166                 :          0 : static struct netconsole_target *alloc_param_target(char *target_config)
     167                 :            : {
     168                 :          0 :         int err = -ENOMEM;
     169                 :          0 :         struct netconsole_target *nt;
     170                 :            : 
     171                 :            :         /*
     172                 :            :          * Allocate and initialize with defaults.
     173                 :            :          * Note that these targets get their config_item fields zeroed-out.
     174                 :            :          */
     175                 :          0 :         nt = kzalloc(sizeof(*nt), GFP_KERNEL);
     176         [ #  # ]:          0 :         if (!nt)
     177                 :          0 :                 goto fail;
     178                 :            : 
     179                 :          0 :         nt->np.name = "netconsole";
     180                 :          0 :         strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
     181                 :          0 :         nt->np.local_port = 6665;
     182                 :          0 :         nt->np.remote_port = 6666;
     183         [ #  # ]:          0 :         eth_broadcast_addr(nt->np.remote_mac);
     184                 :            : 
     185         [ #  # ]:          0 :         if (*target_config == '+') {
     186                 :          0 :                 nt->extended = true;
     187                 :          0 :                 target_config++;
     188                 :            :         }
     189                 :            : 
     190                 :            :         /* Parse parameters and setup netpoll */
     191                 :          0 :         err = netpoll_parse_options(&nt->np, target_config);
     192         [ #  # ]:          0 :         if (err)
     193                 :          0 :                 goto fail;
     194                 :            : 
     195                 :          0 :         err = netpoll_setup(&nt->np);
     196         [ #  # ]:          0 :         if (err)
     197                 :          0 :                 goto fail;
     198                 :            : 
     199                 :          0 :         nt->enabled = true;
     200                 :            : 
     201                 :          0 :         return nt;
     202                 :            : 
     203                 :          0 : fail:
     204                 :          0 :         kfree(nt);
     205                 :          0 :         return ERR_PTR(err);
     206                 :            : }
     207                 :            : 
     208                 :            : /* Cleanup netpoll for given target (from boot/module param) and free it */
     209                 :          0 : static void free_param_target(struct netconsole_target *nt)
     210                 :            : {
     211                 :          0 :         netpoll_cleanup(&nt->np);
     212                 :          0 :         kfree(nt);
     213                 :            : }
     214                 :            : 
     215                 :            : #ifdef  CONFIG_NETCONSOLE_DYNAMIC
     216                 :            : 
     217                 :            : /*
     218                 :            :  * Our subsystem hierarchy is:
     219                 :            :  *
     220                 :            :  * /sys/kernel/config/netconsole/
     221                 :            :  *                              |
     222                 :            :  *                              <target>/
     223                 :            :  *                              |       enabled
     224                 :            :  *                              |       dev_name
     225                 :            :  *                              |       local_port
     226                 :            :  *                              |       remote_port
     227                 :            :  *                              |       local_ip
     228                 :            :  *                              |       remote_ip
     229                 :            :  *                              |       local_mac
     230                 :            :  *                              |       remote_mac
     231                 :            :  *                              |
     232                 :            :  *                              <target>/...
     233                 :            :  */
     234                 :            : 
     235                 :            : static struct netconsole_target *to_target(struct config_item *item)
     236                 :            : {
     237                 :            :         return item ?
     238                 :            :                 container_of(item, struct netconsole_target, item) :
     239                 :            :                 NULL;
     240                 :            : }
     241                 :            : 
     242                 :            : /*
     243                 :            :  * Attribute operations for netconsole_target.
     244                 :            :  */
     245                 :            : 
     246                 :            : static ssize_t enabled_show(struct config_item *item, char *buf)
     247                 :            : {
     248                 :            :         return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->enabled);
     249                 :            : }
     250                 :            : 
     251                 :            : static ssize_t extended_show(struct config_item *item, char *buf)
     252                 :            : {
     253                 :            :         return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->extended);
     254                 :            : }
     255                 :            : 
     256                 :            : static ssize_t dev_name_show(struct config_item *item, char *buf)
     257                 :            : {
     258                 :            :         return snprintf(buf, PAGE_SIZE, "%s\n", to_target(item)->np.dev_name);
     259                 :            : }
     260                 :            : 
     261                 :            : static ssize_t local_port_show(struct config_item *item, char *buf)
     262                 :            : {
     263                 :            :         return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.local_port);
     264                 :            : }
     265                 :            : 
     266                 :            : static ssize_t remote_port_show(struct config_item *item, char *buf)
     267                 :            : {
     268                 :            :         return snprintf(buf, PAGE_SIZE, "%d\n", to_target(item)->np.remote_port);
     269                 :            : }
     270                 :            : 
     271                 :            : static ssize_t local_ip_show(struct config_item *item, char *buf)
     272                 :            : {
     273                 :            :         struct netconsole_target *nt = to_target(item);
     274                 :            : 
     275                 :            :         if (nt->np.ipv6)
     276                 :            :                 return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.local_ip.in6);
     277                 :            :         else
     278                 :            :                 return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip);
     279                 :            : }
     280                 :            : 
     281                 :            : static ssize_t remote_ip_show(struct config_item *item, char *buf)
     282                 :            : {
     283                 :            :         struct netconsole_target *nt = to_target(item);
     284                 :            : 
     285                 :            :         if (nt->np.ipv6)
     286                 :            :                 return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.remote_ip.in6);
     287                 :            :         else
     288                 :            :                 return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip);
     289                 :            : }
     290                 :            : 
     291                 :            : static ssize_t local_mac_show(struct config_item *item, char *buf)
     292                 :            : {
     293                 :            :         struct net_device *dev = to_target(item)->np.dev;
     294                 :            :         static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
     295                 :            : 
     296                 :            :         return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast);
     297                 :            : }
     298                 :            : 
     299                 :            : static ssize_t remote_mac_show(struct config_item *item, char *buf)
     300                 :            : {
     301                 :            :         return snprintf(buf, PAGE_SIZE, "%pM\n", to_target(item)->np.remote_mac);
     302                 :            : }
     303                 :            : 
     304                 :            : /*
     305                 :            :  * This one is special -- targets created through the configfs interface
     306                 :            :  * are not enabled (and the corresponding netpoll activated) by default.
     307                 :            :  * The user is expected to set the desired parameters first (which
     308                 :            :  * would enable him to dynamically add new netpoll targets for new
     309                 :            :  * network interfaces as and when they come up).
     310                 :            :  */
     311                 :            : static ssize_t enabled_store(struct config_item *item,
     312                 :            :                 const char *buf, size_t count)
     313                 :            : {
     314                 :            :         struct netconsole_target *nt = to_target(item);
     315                 :            :         unsigned long flags;
     316                 :            :         int enabled;
     317                 :            :         int err;
     318                 :            : 
     319                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     320                 :            :         err = kstrtoint(buf, 10, &enabled);
     321                 :            :         if (err < 0)
     322                 :            :                 goto out_unlock;
     323                 :            : 
     324                 :            :         err = -EINVAL;
     325                 :            :         if (enabled < 0 || enabled > 1)
     326                 :            :                 goto out_unlock;
     327                 :            :         if ((bool)enabled == nt->enabled) {
     328                 :            :                 pr_info("network logging has already %s\n",
     329                 :            :                         nt->enabled ? "started" : "stopped");
     330                 :            :                 goto out_unlock;
     331                 :            :         }
     332                 :            : 
     333                 :            :         if (enabled) {  /* true */
     334                 :            :                 if (nt->extended && !(netconsole_ext.flags & CON_ENABLED)) {
     335                 :            :                         netconsole_ext.flags |= CON_ENABLED;
     336                 :            :                         register_console(&netconsole_ext);
     337                 :            :                 }
     338                 :            : 
     339                 :            :                 /*
     340                 :            :                  * Skip netpoll_parse_options() -- all the attributes are
     341                 :            :                  * already configured via configfs. Just print them out.
     342                 :            :                  */
     343                 :            :                 netpoll_print_options(&nt->np);
     344                 :            : 
     345                 :            :                 err = netpoll_setup(&nt->np);
     346                 :            :                 if (err)
     347                 :            :                         goto out_unlock;
     348                 :            : 
     349                 :            :                 pr_info("network logging started\n");
     350                 :            :         } else {        /* false */
     351                 :            :                 /* We need to disable the netconsole before cleaning it up
     352                 :            :                  * otherwise we might end up in write_msg() with
     353                 :            :                  * nt->np.dev == NULL and nt->enabled == true
     354                 :            :                  */
     355                 :            :                 spin_lock_irqsave(&target_list_lock, flags);
     356                 :            :                 nt->enabled = false;
     357                 :            :                 spin_unlock_irqrestore(&target_list_lock, flags);
     358                 :            :                 netpoll_cleanup(&nt->np);
     359                 :            :         }
     360                 :            : 
     361                 :            :         nt->enabled = enabled;
     362                 :            : 
     363                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     364                 :            :         return strnlen(buf, count);
     365                 :            : out_unlock:
     366                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     367                 :            :         return err;
     368                 :            : }
     369                 :            : 
     370                 :            : static ssize_t extended_store(struct config_item *item, const char *buf,
     371                 :            :                 size_t count)
     372                 :            : {
     373                 :            :         struct netconsole_target *nt = to_target(item);
     374                 :            :         int extended;
     375                 :            :         int err;
     376                 :            : 
     377                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     378                 :            :         if (nt->enabled) {
     379                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     380                 :            :                        config_item_name(&nt->item));
     381                 :            :                 err = -EINVAL;
     382                 :            :                 goto out_unlock;
     383                 :            :         }
     384                 :            : 
     385                 :            :         err = kstrtoint(buf, 10, &extended);
     386                 :            :         if (err < 0)
     387                 :            :                 goto out_unlock;
     388                 :            :         if (extended < 0 || extended > 1) {
     389                 :            :                 err = -EINVAL;
     390                 :            :                 goto out_unlock;
     391                 :            :         }
     392                 :            : 
     393                 :            :         nt->extended = extended;
     394                 :            : 
     395                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     396                 :            :         return strnlen(buf, count);
     397                 :            : out_unlock:
     398                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     399                 :            :         return err;
     400                 :            : }
     401                 :            : 
     402                 :            : static ssize_t dev_name_store(struct config_item *item, const char *buf,
     403                 :            :                 size_t count)
     404                 :            : {
     405                 :            :         struct netconsole_target *nt = to_target(item);
     406                 :            :         size_t len;
     407                 :            : 
     408                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     409                 :            :         if (nt->enabled) {
     410                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     411                 :            :                        config_item_name(&nt->item));
     412                 :            :                 mutex_unlock(&dynamic_netconsole_mutex);
     413                 :            :                 return -EINVAL;
     414                 :            :         }
     415                 :            : 
     416                 :            :         strlcpy(nt->np.dev_name, buf, IFNAMSIZ);
     417                 :            : 
     418                 :            :         /* Get rid of possible trailing newline from echo(1) */
     419                 :            :         len = strnlen(nt->np.dev_name, IFNAMSIZ);
     420                 :            :         if (nt->np.dev_name[len - 1] == '\n')
     421                 :            :                 nt->np.dev_name[len - 1] = '\0';
     422                 :            : 
     423                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     424                 :            :         return strnlen(buf, count);
     425                 :            : }
     426                 :            : 
     427                 :            : static ssize_t local_port_store(struct config_item *item, const char *buf,
     428                 :            :                 size_t count)
     429                 :            : {
     430                 :            :         struct netconsole_target *nt = to_target(item);
     431                 :            :         int rv = -EINVAL;
     432                 :            : 
     433                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     434                 :            :         if (nt->enabled) {
     435                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     436                 :            :                        config_item_name(&nt->item));
     437                 :            :                 goto out_unlock;
     438                 :            :         }
     439                 :            : 
     440                 :            :         rv = kstrtou16(buf, 10, &nt->np.local_port);
     441                 :            :         if (rv < 0)
     442                 :            :                 goto out_unlock;
     443                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     444                 :            :         return strnlen(buf, count);
     445                 :            : out_unlock:
     446                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     447                 :            :         return rv;
     448                 :            : }
     449                 :            : 
     450                 :            : static ssize_t remote_port_store(struct config_item *item,
     451                 :            :                 const char *buf, size_t count)
     452                 :            : {
     453                 :            :         struct netconsole_target *nt = to_target(item);
     454                 :            :         int rv = -EINVAL;
     455                 :            : 
     456                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     457                 :            :         if (nt->enabled) {
     458                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     459                 :            :                        config_item_name(&nt->item));
     460                 :            :                 goto out_unlock;
     461                 :            :         }
     462                 :            : 
     463                 :            :         rv = kstrtou16(buf, 10, &nt->np.remote_port);
     464                 :            :         if (rv < 0)
     465                 :            :                 goto out_unlock;
     466                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     467                 :            :         return strnlen(buf, count);
     468                 :            : out_unlock:
     469                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     470                 :            :         return rv;
     471                 :            : }
     472                 :            : 
     473                 :            : static ssize_t local_ip_store(struct config_item *item, const char *buf,
     474                 :            :                 size_t count)
     475                 :            : {
     476                 :            :         struct netconsole_target *nt = to_target(item);
     477                 :            : 
     478                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     479                 :            :         if (nt->enabled) {
     480                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     481                 :            :                        config_item_name(&nt->item));
     482                 :            :                 goto out_unlock;
     483                 :            :         }
     484                 :            : 
     485                 :            :         if (strnchr(buf, count, ':')) {
     486                 :            :                 const char *end;
     487                 :            :                 if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) {
     488                 :            :                         if (*end && *end != '\n') {
     489                 :            :                                 pr_err("invalid IPv6 address at: <%c>\n", *end);
     490                 :            :                                 goto out_unlock;
     491                 :            :                         }
     492                 :            :                         nt->np.ipv6 = true;
     493                 :            :                 } else
     494                 :            :                         goto out_unlock;
     495                 :            :         } else {
     496                 :            :                 if (!nt->np.ipv6) {
     497                 :            :                         nt->np.local_ip.ip = in_aton(buf);
     498                 :            :                 } else
     499                 :            :                         goto out_unlock;
     500                 :            :         }
     501                 :            : 
     502                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     503                 :            :         return strnlen(buf, count);
     504                 :            : out_unlock:
     505                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     506                 :            :         return -EINVAL;
     507                 :            : }
     508                 :            : 
     509                 :            : static ssize_t remote_ip_store(struct config_item *item, const char *buf,
     510                 :            :                size_t count)
     511                 :            : {
     512                 :            :         struct netconsole_target *nt = to_target(item);
     513                 :            : 
     514                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     515                 :            :         if (nt->enabled) {
     516                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     517                 :            :                        config_item_name(&nt->item));
     518                 :            :                 goto out_unlock;
     519                 :            :         }
     520                 :            : 
     521                 :            :         if (strnchr(buf, count, ':')) {
     522                 :            :                 const char *end;
     523                 :            :                 if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) {
     524                 :            :                         if (*end && *end != '\n') {
     525                 :            :                                 pr_err("invalid IPv6 address at: <%c>\n", *end);
     526                 :            :                                 goto out_unlock;
     527                 :            :                         }
     528                 :            :                         nt->np.ipv6 = true;
     529                 :            :                 } else
     530                 :            :                         goto out_unlock;
     531                 :            :         } else {
     532                 :            :                 if (!nt->np.ipv6) {
     533                 :            :                         nt->np.remote_ip.ip = in_aton(buf);
     534                 :            :                 } else
     535                 :            :                         goto out_unlock;
     536                 :            :         }
     537                 :            : 
     538                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     539                 :            :         return strnlen(buf, count);
     540                 :            : out_unlock:
     541                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     542                 :            :         return -EINVAL;
     543                 :            : }
     544                 :            : 
     545                 :            : static ssize_t remote_mac_store(struct config_item *item, const char *buf,
     546                 :            :                 size_t count)
     547                 :            : {
     548                 :            :         struct netconsole_target *nt = to_target(item);
     549                 :            :         u8 remote_mac[ETH_ALEN];
     550                 :            : 
     551                 :            :         mutex_lock(&dynamic_netconsole_mutex);
     552                 :            :         if (nt->enabled) {
     553                 :            :                 pr_err("target (%s) is enabled, disable to update parameters\n",
     554                 :            :                        config_item_name(&nt->item));
     555                 :            :                 goto out_unlock;
     556                 :            :         }
     557                 :            : 
     558                 :            :         if (!mac_pton(buf, remote_mac))
     559                 :            :                 goto out_unlock;
     560                 :            :         if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
     561                 :            :                 goto out_unlock;
     562                 :            :         memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
     563                 :            : 
     564                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     565                 :            :         return strnlen(buf, count);
     566                 :            : out_unlock:
     567                 :            :         mutex_unlock(&dynamic_netconsole_mutex);
     568                 :            :         return -EINVAL;
     569                 :            : }
     570                 :            : 
     571                 :            : CONFIGFS_ATTR(, enabled);
     572                 :            : CONFIGFS_ATTR(, extended);
     573                 :            : CONFIGFS_ATTR(, dev_name);
     574                 :            : CONFIGFS_ATTR(, local_port);
     575                 :            : CONFIGFS_ATTR(, remote_port);
     576                 :            : CONFIGFS_ATTR(, local_ip);
     577                 :            : CONFIGFS_ATTR(, remote_ip);
     578                 :            : CONFIGFS_ATTR_RO(, local_mac);
     579                 :            : CONFIGFS_ATTR(, remote_mac);
     580                 :            : 
     581                 :            : static struct configfs_attribute *netconsole_target_attrs[] = {
     582                 :            :         &attr_enabled,
     583                 :            :         &attr_extended,
     584                 :            :         &attr_dev_name,
     585                 :            :         &attr_local_port,
     586                 :            :         &attr_remote_port,
     587                 :            :         &attr_local_ip,
     588                 :            :         &attr_remote_ip,
     589                 :            :         &attr_local_mac,
     590                 :            :         &attr_remote_mac,
     591                 :            :         NULL,
     592                 :            : };
     593                 :            : 
     594                 :            : /*
     595                 :            :  * Item operations and type for netconsole_target.
     596                 :            :  */
     597                 :            : 
     598                 :            : static void netconsole_target_release(struct config_item *item)
     599                 :            : {
     600                 :            :         kfree(to_target(item));
     601                 :            : }
     602                 :            : 
     603                 :            : static struct configfs_item_operations netconsole_target_item_ops = {
     604                 :            :         .release                = netconsole_target_release,
     605                 :            : };
     606                 :            : 
     607                 :            : static const struct config_item_type netconsole_target_type = {
     608                 :            :         .ct_attrs               = netconsole_target_attrs,
     609                 :            :         .ct_item_ops            = &netconsole_target_item_ops,
     610                 :            :         .ct_owner               = THIS_MODULE,
     611                 :            : };
     612                 :            : 
     613                 :            : /*
     614                 :            :  * Group operations and type for netconsole_subsys.
     615                 :            :  */
     616                 :            : 
     617                 :            : static struct config_item *make_netconsole_target(struct config_group *group,
     618                 :            :                                                   const char *name)
     619                 :            : {
     620                 :            :         unsigned long flags;
     621                 :            :         struct netconsole_target *nt;
     622                 :            : 
     623                 :            :         /*
     624                 :            :          * Allocate and initialize with defaults.
     625                 :            :          * Target is disabled at creation (!enabled).
     626                 :            :          */
     627                 :            :         nt = kzalloc(sizeof(*nt), GFP_KERNEL);
     628                 :            :         if (!nt)
     629                 :            :                 return ERR_PTR(-ENOMEM);
     630                 :            : 
     631                 :            :         nt->np.name = "netconsole";
     632                 :            :         strlcpy(nt->np.dev_name, "eth0", IFNAMSIZ);
     633                 :            :         nt->np.local_port = 6665;
     634                 :            :         nt->np.remote_port = 6666;
     635                 :            :         eth_broadcast_addr(nt->np.remote_mac);
     636                 :            : 
     637                 :            :         /* Initialize the config_item member */
     638                 :            :         config_item_init_type_name(&nt->item, name, &netconsole_target_type);
     639                 :            : 
     640                 :            :         /* Adding, but it is disabled */
     641                 :            :         spin_lock_irqsave(&target_list_lock, flags);
     642                 :            :         list_add(&nt->list, &target_list);
     643                 :            :         spin_unlock_irqrestore(&target_list_lock, flags);
     644                 :            : 
     645                 :            :         return &nt->item;
     646                 :            : }
     647                 :            : 
     648                 :            : static void drop_netconsole_target(struct config_group *group,
     649                 :            :                                    struct config_item *item)
     650                 :            : {
     651                 :            :         unsigned long flags;
     652                 :            :         struct netconsole_target *nt = to_target(item);
     653                 :            : 
     654                 :            :         spin_lock_irqsave(&target_list_lock, flags);
     655                 :            :         list_del(&nt->list);
     656                 :            :         spin_unlock_irqrestore(&target_list_lock, flags);
     657                 :            : 
     658                 :            :         /*
     659                 :            :          * The target may have never been enabled, or was manually disabled
     660                 :            :          * before being removed so netpoll may have already been cleaned up.
     661                 :            :          */
     662                 :            :         if (nt->enabled)
     663                 :            :                 netpoll_cleanup(&nt->np);
     664                 :            : 
     665                 :            :         config_item_put(&nt->item);
     666                 :            : }
     667                 :            : 
     668                 :            : static struct configfs_group_operations netconsole_subsys_group_ops = {
     669                 :            :         .make_item      = make_netconsole_target,
     670                 :            :         .drop_item      = drop_netconsole_target,
     671                 :            : };
     672                 :            : 
     673                 :            : static const struct config_item_type netconsole_subsys_type = {
     674                 :            :         .ct_group_ops   = &netconsole_subsys_group_ops,
     675                 :            :         .ct_owner       = THIS_MODULE,
     676                 :            : };
     677                 :            : 
     678                 :            : /* The netconsole configfs subsystem */
     679                 :            : static struct configfs_subsystem netconsole_subsys = {
     680                 :            :         .su_group       = {
     681                 :            :                 .cg_item        = {
     682                 :            :                         .ci_namebuf     = "netconsole",
     683                 :            :                         .ci_type        = &netconsole_subsys_type,
     684                 :            :                 },
     685                 :            :         },
     686                 :            : };
     687                 :            : 
     688                 :            : #endif  /* CONFIG_NETCONSOLE_DYNAMIC */
     689                 :            : 
     690                 :            : /* Handle network interface device notifications */
     691                 :         52 : static int netconsole_netdev_event(struct notifier_block *this,
     692                 :            :                                    unsigned long event, void *ptr)
     693                 :            : {
     694                 :         52 :         unsigned long flags;
     695                 :         52 :         struct netconsole_target *nt;
     696         [ +  - ]:         52 :         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
     697                 :         52 :         bool stopped = false;
     698                 :            : 
     699         [ +  - ]:         52 :         if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
     700         [ +  - ]:         52 :               event == NETDEV_RELEASE || event == NETDEV_JOIN))
     701                 :         52 :                 goto done;
     702                 :            : 
     703                 :          0 :         spin_lock_irqsave(&target_list_lock, flags);
     704                 :          0 : restart:
     705         [ #  # ]:          0 :         list_for_each_entry(nt, &target_list, list) {
     706                 :          0 :                 netconsole_target_get(nt);
     707         [ #  # ]:          0 :                 if (nt->np.dev == dev) {
     708      [ #  #  # ]:          0 :                         switch (event) {
     709                 :          0 :                         case NETDEV_CHANGENAME:
     710                 :          0 :                                 strlcpy(nt->np.dev_name, dev->name, IFNAMSIZ);
     711                 :          0 :                                 break;
     712                 :            :                         case NETDEV_RELEASE:
     713                 :            :                         case NETDEV_JOIN:
     714                 :            :                         case NETDEV_UNREGISTER:
     715                 :            :                                 /* rtnl_lock already held
     716                 :            :                                  * we might sleep in __netpoll_cleanup()
     717                 :            :                                  */
     718                 :          0 :                                 spin_unlock_irqrestore(&target_list_lock, flags);
     719                 :            : 
     720                 :          0 :                                 __netpoll_cleanup(&nt->np);
     721                 :            : 
     722                 :          0 :                                 spin_lock_irqsave(&target_list_lock, flags);
     723                 :          0 :                                 dev_put(nt->np.dev);
     724                 :          0 :                                 nt->np.dev = NULL;
     725                 :          0 :                                 nt->enabled = false;
     726                 :          0 :                                 stopped = true;
     727                 :          0 :                                 netconsole_target_put(nt);
     728                 :          0 :                                 goto restart;
     729                 :            :                         }
     730                 :          0 :                 }
     731                 :          0 :                 netconsole_target_put(nt);
     732                 :            :         }
     733                 :          0 :         spin_unlock_irqrestore(&target_list_lock, flags);
     734         [ #  # ]:          0 :         if (stopped) {
     735                 :          0 :                 const char *msg = "had an event";
     736         [ #  # ]:          0 :                 switch (event) {
     737                 :            :                 case NETDEV_UNREGISTER:
     738                 :            :                         msg = "unregistered";
     739                 :            :                         break;
     740                 :            :                 case NETDEV_RELEASE:
     741                 :            :                         msg = "released slaves";
     742                 :            :                         break;
     743                 :            :                 case NETDEV_JOIN:
     744                 :            :                         msg = "is joining a master device";
     745                 :            :                         break;
     746                 :            :                 }
     747                 :          0 :                 pr_info("network logging stopped on interface %s as it %s\n",
     748                 :            :                         dev->name, msg);
     749                 :            :         }
     750                 :            : 
     751                 :          0 : done:
     752                 :         52 :         return NOTIFY_DONE;
     753                 :            : }
     754                 :            : 
     755                 :            : static struct notifier_block netconsole_netdev_notifier = {
     756                 :            :         .notifier_call  = netconsole_netdev_event,
     757                 :            : };
     758                 :            : 
     759                 :            : /**
     760                 :            :  * send_ext_msg_udp - send extended log message to target
     761                 :            :  * @nt: target to send message to
     762                 :            :  * @msg: extended log message to send
     763                 :            :  * @msg_len: length of message
     764                 :            :  *
     765                 :            :  * Transfer extended log @msg to @nt.  If @msg is longer than
     766                 :            :  * MAX_PRINT_CHUNK, it'll be split and transmitted in multiple chunks with
     767                 :            :  * ncfrag header field added to identify them.
     768                 :            :  */
     769                 :          0 : static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
     770                 :            :                              int msg_len)
     771                 :            : {
     772                 :          0 :         static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */
     773                 :          0 :         const char *header, *body;
     774                 :          0 :         int offset = 0;
     775                 :          0 :         int header_len, body_len;
     776                 :            : 
     777         [ #  # ]:          0 :         if (msg_len <= MAX_PRINT_CHUNK) {
     778                 :          0 :                 netpoll_send_udp(&nt->np, msg, msg_len);
     779                 :          0 :                 return;
     780                 :            :         }
     781                 :            : 
     782                 :            :         /* need to insert extra header fields, detect header and body */
     783                 :          0 :         header = msg;
     784                 :          0 :         body = memchr(msg, ';', msg_len);
     785   [ #  #  #  # ]:          0 :         if (WARN_ON_ONCE(!body))
     786                 :            :                 return;
     787                 :            : 
     788                 :          0 :         header_len = body - header;
     789                 :          0 :         body_len = msg_len - header_len - 1;
     790                 :          0 :         body++;
     791                 :            : 
     792                 :            :         /*
     793                 :            :          * Transfer multiple chunks with the following extra header.
     794                 :            :          * "ncfrag=<byte-offset>/<total-bytes>"
     795                 :            :          */
     796                 :          0 :         memcpy(buf, header, header_len);
     797                 :            : 
     798         [ #  # ]:          0 :         while (offset < body_len) {
     799                 :          0 :                 int this_header = header_len;
     800                 :          0 :                 int this_chunk;
     801                 :            : 
     802                 :          0 :                 this_header += scnprintf(buf + this_header,
     803                 :            :                                          sizeof(buf) - this_header,
     804                 :            :                                          ",ncfrag=%d/%d;", offset, body_len);
     805                 :            : 
     806                 :          0 :                 this_chunk = min(body_len - offset,
     807                 :            :                                  MAX_PRINT_CHUNK - this_header);
     808   [ #  #  #  # ]:          0 :                 if (WARN_ON_ONCE(this_chunk <= 0))
     809                 :            :                         return;
     810                 :            : 
     811                 :          0 :                 memcpy(buf + this_header, body + offset, this_chunk);
     812                 :            : 
     813                 :          0 :                 netpoll_send_udp(&nt->np, buf, this_header + this_chunk);
     814                 :            : 
     815                 :          0 :                 offset += this_chunk;
     816                 :            :         }
     817                 :            : }
     818                 :            : 
     819                 :          0 : static void write_ext_msg(struct console *con, const char *msg,
     820                 :            :                           unsigned int len)
     821                 :            : {
     822                 :          0 :         struct netconsole_target *nt;
     823                 :          0 :         unsigned long flags;
     824                 :            : 
     825   [ #  #  #  #  :          0 :         if ((oops_only && !oops_in_progress) || list_empty(&target_list))
                   #  # ]
     826                 :            :                 return;
     827                 :            : 
     828                 :          0 :         spin_lock_irqsave(&target_list_lock, flags);
     829         [ #  # ]:          0 :         list_for_each_entry(nt, &target_list, list)
     830   [ #  #  #  #  :          0 :                 if (nt->extended && nt->enabled && netif_running(nt->np.dev))
                   #  # ]
     831                 :          0 :                         send_ext_msg_udp(nt, msg, len);
     832                 :          0 :         spin_unlock_irqrestore(&target_list_lock, flags);
     833                 :            : }
     834                 :            : 
     835                 :        984 : static void write_msg(struct console *con, const char *msg, unsigned int len)
     836                 :            : {
     837                 :        984 :         int frag, left;
     838                 :        984 :         unsigned long flags;
     839                 :        984 :         struct netconsole_target *nt;
     840                 :        984 :         const char *tmp;
     841                 :            : 
     842   [ -  +  -  - ]:        984 :         if (oops_only && !oops_in_progress)
     843                 :            :                 return;
     844                 :            :         /* Avoid taking lock and disabling interrupts unnecessarily */
     845         [ -  + ]:        984 :         if (list_empty(&target_list))
     846                 :            :                 return;
     847                 :            : 
     848                 :          0 :         spin_lock_irqsave(&target_list_lock, flags);
     849         [ #  # ]:          0 :         list_for_each_entry(nt, &target_list, list) {
     850   [ #  #  #  #  :          0 :                 if (!nt->extended && nt->enabled && netif_running(nt->np.dev)) {
                   #  # ]
     851                 :            :                         /*
     852                 :            :                          * We nest this inside the for-each-target loop above
     853                 :            :                          * so that we're able to get as much logging out to
     854                 :            :                          * at least one target if we die inside here, instead
     855                 :            :                          * of unnecessarily keeping all targets in lock-step.
     856                 :            :                          */
     857                 :          0 :                         tmp = msg;
     858         [ #  # ]:          0 :                         for (left = len; left;) {
     859                 :          0 :                                 frag = min(left, MAX_PRINT_CHUNK);
     860                 :          0 :                                 netpoll_send_udp(&nt->np, tmp, frag);
     861                 :          0 :                                 tmp += frag;
     862                 :          0 :                                 left -= frag;
     863                 :            :                         }
     864                 :            :                 }
     865                 :            :         }
     866                 :          0 :         spin_unlock_irqrestore(&target_list_lock, flags);
     867                 :            : }
     868                 :            : 
     869                 :            : static struct console netconsole_ext = {
     870                 :            :         .name   = "netcon_ext",
     871                 :            :         .flags  = CON_EXTENDED, /* starts disabled, registered on first use */
     872                 :            :         .write  = write_ext_msg,
     873                 :            : };
     874                 :            : 
     875                 :            : static struct console netconsole = {
     876                 :            :         .name   = "netcon",
     877                 :            :         .flags  = CON_ENABLED,
     878                 :            :         .write  = write_msg,
     879                 :            : };
     880                 :            : 
     881                 :         13 : static int __init init_netconsole(void)
     882                 :            : {
     883                 :         13 :         int err;
     884                 :         13 :         struct netconsole_target *nt, *tmp;
     885                 :         13 :         unsigned long flags;
     886                 :         13 :         char *target_config;
     887                 :         13 :         char *input = config;
     888                 :            : 
     889         [ -  + ]:         13 :         if (strnlen(input, MAX_PARAM_LENGTH)) {
     890         [ #  # ]:          0 :                 while ((target_config = strsep(&input, ";"))) {
     891                 :          0 :                         nt = alloc_param_target(target_config);
     892         [ #  # ]:          0 :                         if (IS_ERR(nt)) {
     893                 :          0 :                                 err = PTR_ERR(nt);
     894                 :          0 :                                 goto fail;
     895                 :            :                         }
     896                 :            :                         /* Dump existing printks when we register */
     897         [ #  # ]:          0 :                         if (nt->extended)
     898                 :          0 :                                 netconsole_ext.flags |= CON_PRINTBUFFER |
     899                 :            :                                                         CON_ENABLED;
     900                 :            :                         else
     901                 :          0 :                                 netconsole.flags |= CON_PRINTBUFFER;
     902                 :            : 
     903                 :          0 :                         spin_lock_irqsave(&target_list_lock, flags);
     904                 :          0 :                         list_add(&nt->list, &target_list);
     905                 :          0 :                         spin_unlock_irqrestore(&target_list_lock, flags);
     906                 :            :                 }
     907                 :            :         }
     908                 :            : 
     909                 :         13 :         err = register_netdevice_notifier(&netconsole_netdev_notifier);
     910         [ -  + ]:         13 :         if (err)
     911                 :          0 :                 goto fail;
     912                 :            : 
     913                 :         13 :         err = dynamic_netconsole_init();
     914                 :         13 :         if (err)
     915                 :            :                 goto undonotifier;
     916                 :            : 
     917         [ -  + ]:         13 :         if (netconsole_ext.flags & CON_ENABLED)
     918                 :          0 :                 register_console(&netconsole_ext);
     919                 :         13 :         register_console(&netconsole);
     920                 :         13 :         pr_info("network logging started\n");
     921                 :            : 
     922                 :         13 :         return err;
     923                 :            : 
     924                 :            : undonotifier:
     925                 :            :         unregister_netdevice_notifier(&netconsole_netdev_notifier);
     926                 :            : 
     927                 :          0 : fail:
     928                 :          0 :         pr_err("cleaning up\n");
     929                 :            : 
     930                 :            :         /*
     931                 :            :          * Remove all targets and destroy them (only targets created
     932                 :            :          * from the boot/module option exist here). Skipping the list
     933                 :            :          * lock is safe here, and netpoll_cleanup() will sleep.
     934                 :            :          */
     935         [ #  # ]:          0 :         list_for_each_entry_safe(nt, tmp, &target_list, list) {
     936                 :          0 :                 list_del(&nt->list);
     937                 :          0 :                 free_param_target(nt);
     938                 :            :         }
     939                 :            : 
     940                 :            :         return err;
     941                 :            : }
     942                 :            : 
     943                 :          0 : static void __exit cleanup_netconsole(void)
     944                 :            : {
     945                 :          0 :         struct netconsole_target *nt, *tmp;
     946                 :            : 
     947                 :          0 :         unregister_console(&netconsole_ext);
     948                 :          0 :         unregister_console(&netconsole);
     949                 :          0 :         dynamic_netconsole_exit();
     950                 :          0 :         unregister_netdevice_notifier(&netconsole_netdev_notifier);
     951                 :            : 
     952                 :            :         /*
     953                 :            :          * Targets created via configfs pin references on our module
     954                 :            :          * and would first be rmdir(2)'ed from userspace. We reach
     955                 :            :          * here only when they are already destroyed, and only those
     956                 :            :          * created from the boot/module option are left, so remove and
     957                 :            :          * destroy them. Skipping the list lock is safe here, and
     958                 :            :          * netpoll_cleanup() will sleep.
     959                 :            :          */
     960         [ #  # ]:          0 :         list_for_each_entry_safe(nt, tmp, &target_list, list) {
     961                 :          0 :                 list_del(&nt->list);
     962                 :          0 :                 free_param_target(nt);
     963                 :            :         }
     964                 :          0 : }
     965                 :            : 
     966                 :            : /*
     967                 :            :  * Use late_initcall to ensure netconsole is
     968                 :            :  * initialized after network device driver if built-in.
     969                 :            :  *
     970                 :            :  * late_initcall() and module_init() are identical if built as module.
     971                 :            :  */
     972                 :            : late_initcall(init_netconsole);
     973                 :            : module_exit(cleanup_netconsole);

Generated by: LCOV version 1.14