LCOV - code coverage report
Current view: top level - net/ethtool - netlink.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 6 333 1.8 %
Date: 2022-04-01 14:58:12 Functions: 1 13 7.7 %
Branches: 2 208 1.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : 
       3                 :            : #include <net/sock.h>
       4                 :            : #include <linux/ethtool_netlink.h>
       5                 :            : #include "netlink.h"
       6                 :            : 
       7                 :            : static struct genl_family ethtool_genl_family;
       8                 :            : 
       9                 :            : static bool ethnl_ok __read_mostly;
      10                 :            : static u32 ethnl_bcast_seq;
      11                 :            : 
      12                 :            : static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = {
      13                 :            :         [ETHTOOL_A_HEADER_UNSPEC]       = { .type = NLA_REJECT },
      14                 :            :         [ETHTOOL_A_HEADER_DEV_INDEX]    = { .type = NLA_U32 },
      15                 :            :         [ETHTOOL_A_HEADER_DEV_NAME]     = { .type = NLA_NUL_STRING,
      16                 :            :                                             .len = ALTIFNAMSIZ - 1 },
      17                 :            :         [ETHTOOL_A_HEADER_FLAGS]        = { .type = NLA_U32 },
      18                 :            : };
      19                 :            : 
      20                 :            : /**
      21                 :            :  * ethnl_parse_header() - parse request header
      22                 :            :  * @req_info:    structure to put results into
      23                 :            :  * @header:      nest attribute with request header
      24                 :            :  * @net:         request netns
      25                 :            :  * @extack:      netlink extack for error reporting
      26                 :            :  * @require_dev: fail if no device identified in header
      27                 :            :  *
      28                 :            :  * Parse request header in nested attribute @nest and puts results into
      29                 :            :  * the structure pointed to by @req_info. Extack from @info is used for error
      30                 :            :  * reporting. If req_info->dev is not null on return, reference to it has
      31                 :            :  * been taken. If error is returned, *req_info is null initialized and no
      32                 :            :  * reference is held.
      33                 :            :  *
      34                 :            :  * Return: 0 on success or negative error code
      35                 :            :  */
      36                 :          0 : int ethnl_parse_header(struct ethnl_req_info *req_info,
      37                 :            :                        const struct nlattr *header, struct net *net,
      38                 :            :                        struct netlink_ext_ack *extack, bool require_dev)
      39                 :            : {
      40                 :          0 :         struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1];
      41                 :          0 :         const struct nlattr *devname_attr;
      42                 :          0 :         struct net_device *dev = NULL;
      43                 :          0 :         u32 flags = 0;
      44                 :          0 :         int ret;
      45                 :            : 
      46         [ #  # ]:          0 :         if (!header) {
      47         [ #  # ]:          0 :                 NL_SET_ERR_MSG(extack, "request header missing");
      48                 :          0 :                 return -EINVAL;
      49                 :            :         }
      50                 :          0 :         ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
      51                 :            :                                ethnl_header_policy, extack);
      52         [ #  # ]:          0 :         if (ret < 0)
      53                 :            :                 return ret;
      54         [ #  # ]:          0 :         if (tb[ETHTOOL_A_HEADER_FLAGS]) {
      55         [ #  # ]:          0 :                 flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
      56         [ #  # ]:          0 :                 if (flags & ~ETHTOOL_FLAG_ALL) {
      57         [ #  # ]:          0 :                         NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_HEADER_FLAGS],
      58                 :            :                                             "unrecognized request flags");
      59         [ #  # ]:          0 :                         nl_set_extack_cookie_u32(extack, ETHTOOL_FLAG_ALL);
      60                 :          0 :                         return -EOPNOTSUPP;
      61                 :            :                 }
      62                 :            :         }
      63                 :            : 
      64                 :          0 :         devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
      65         [ #  # ]:          0 :         if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
      66                 :          0 :                 u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]);
      67                 :            : 
      68                 :          0 :                 dev = dev_get_by_index(net, ifindex);
      69         [ #  # ]:          0 :                 if (!dev) {
      70         [ #  # ]:          0 :                         NL_SET_ERR_MSG_ATTR(extack,
      71                 :            :                                             tb[ETHTOOL_A_HEADER_DEV_INDEX],
      72                 :            :                                             "no device matches ifindex");
      73                 :          0 :                         return -ENODEV;
      74                 :            :                 }
      75                 :            :                 /* if both ifindex and ifname are passed, they must match */
      76         [ #  # ]:          0 :                 if (devname_attr &&
      77         [ #  # ]:          0 :                     strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) {
      78                 :          0 :                         dev_put(dev);
      79         [ #  # ]:          0 :                         NL_SET_ERR_MSG_ATTR(extack, header,
      80                 :            :                                             "ifindex and name do not match");
      81                 :          0 :                         return -ENODEV;
      82                 :            :                 }
      83         [ #  # ]:          0 :         } else if (devname_attr) {
      84                 :          0 :                 dev = dev_get_by_name(net, nla_data(devname_attr));
      85         [ #  # ]:          0 :                 if (!dev) {
      86         [ #  # ]:          0 :                         NL_SET_ERR_MSG_ATTR(extack, devname_attr,
      87                 :            :                                             "no device matches name");
      88                 :          0 :                         return -ENODEV;
      89                 :            :                 }
      90         [ #  # ]:          0 :         } else if (require_dev) {
      91         [ #  # ]:          0 :                 NL_SET_ERR_MSG_ATTR(extack, header,
      92                 :            :                                     "neither ifindex nor name specified");
      93                 :          0 :                 return -EINVAL;
      94                 :            :         }
      95                 :            : 
      96   [ #  #  #  # ]:          0 :         if (dev && !netif_device_present(dev)) {
      97                 :          0 :                 dev_put(dev);
      98         [ #  # ]:          0 :                 NL_SET_ERR_MSG(extack, "device not present");
      99                 :          0 :                 return -ENODEV;
     100                 :            :         }
     101                 :            : 
     102                 :          0 :         req_info->dev = dev;
     103                 :          0 :         req_info->flags = flags;
     104                 :          0 :         return 0;
     105                 :            : }
     106                 :            : 
     107                 :            : /**
     108                 :            :  * ethnl_fill_reply_header() - Put common header into a reply message
     109                 :            :  * @skb:      skb with the message
     110                 :            :  * @dev:      network device to describe in header
     111                 :            :  * @attrtype: attribute type to use for the nest
     112                 :            :  *
     113                 :            :  * Create a nested attribute with attributes describing given network device.
     114                 :            :  *
     115                 :            :  * Return: 0 on success, error value (-EMSGSIZE only) on error
     116                 :            :  */
     117                 :          0 : int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev,
     118                 :            :                             u16 attrtype)
     119                 :            : {
     120                 :          0 :         struct nlattr *nest;
     121                 :            : 
     122         [ #  # ]:          0 :         if (!dev)
     123                 :            :                 return 0;
     124                 :          0 :         nest = nla_nest_start(skb, attrtype);
     125         [ #  # ]:          0 :         if (!nest)
     126                 :            :                 return -EMSGSIZE;
     127                 :            : 
     128   [ #  #  #  # ]:          0 :         if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) ||
     129                 :          0 :             nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name))
     130                 :          0 :                 goto nla_put_failure;
     131                 :            :         /* If more attributes are put into reply header, ethnl_header_size()
     132                 :            :          * must be updated to account for them.
     133                 :            :          */
     134                 :            : 
     135                 :          0 :         nla_nest_end(skb, nest);
     136                 :          0 :         return 0;
     137                 :            : 
     138                 :            : nla_put_failure:
     139                 :          0 :         nla_nest_cancel(skb, nest);
     140                 :          0 :         return -EMSGSIZE;
     141                 :            : }
     142                 :            : 
     143                 :            : /**
     144                 :            :  * ethnl_reply_init() - Create skb for a reply and fill device identification
     145                 :            :  * @payload:      payload length (without netlink and genetlink header)
     146                 :            :  * @dev:          device the reply is about (may be null)
     147                 :            :  * @cmd:          ETHTOOL_MSG_* message type for reply
     148                 :            :  * @hdr_attrtype: attribute type for common header
     149                 :            :  * @info:         genetlink info of the received packet we respond to
     150                 :            :  * @ehdrp:        place to store payload pointer returned by genlmsg_new()
     151                 :            :  *
     152                 :            :  * Return: pointer to allocated skb on success, NULL on error
     153                 :            :  */
     154                 :          0 : struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
     155                 :            :                                  u16 hdr_attrtype, struct genl_info *info,
     156                 :            :                                  void **ehdrp)
     157                 :            : {
     158                 :          0 :         struct sk_buff *skb;
     159                 :            : 
     160                 :          0 :         skb = genlmsg_new(payload, GFP_KERNEL);
     161         [ #  # ]:          0 :         if (!skb)
     162                 :          0 :                 goto err;
     163                 :          0 :         *ehdrp = genlmsg_put_reply(skb, info, &ethtool_genl_family, 0, cmd);
     164         [ #  # ]:          0 :         if (!*ehdrp)
     165                 :          0 :                 goto err_free;
     166                 :            : 
     167         [ #  # ]:          0 :         if (dev) {
     168                 :          0 :                 int ret;
     169                 :            : 
     170                 :          0 :                 ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype);
     171         [ #  # ]:          0 :                 if (ret < 0)
     172                 :          0 :                         goto err_free;
     173                 :            :         }
     174                 :            :         return skb;
     175                 :            : 
     176                 :          0 : err_free:
     177                 :          0 :         nlmsg_free(skb);
     178                 :          0 : err:
     179         [ #  # ]:          0 :         if (info)
     180         [ #  # ]:          0 :                 GENL_SET_ERR_MSG(info, "failed to setup reply message");
     181                 :            :         return NULL;
     182                 :            : }
     183                 :            : 
     184                 :          0 : static void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd)
     185                 :            : {
     186                 :          0 :         return genlmsg_put(skb, 0, ++ethnl_bcast_seq, &ethtool_genl_family, 0,
     187                 :            :                            cmd);
     188                 :            : }
     189                 :            : 
     190                 :          0 : static int ethnl_multicast(struct sk_buff *skb, struct net_device *dev)
     191                 :            : {
     192                 :          0 :         return genlmsg_multicast_netns(&ethtool_genl_family, dev_net(dev), skb,
     193                 :            :                                        0, ETHNL_MCGRP_MONITOR, GFP_KERNEL);
     194                 :            : }
     195                 :            : 
     196                 :            : /* GET request helpers */
     197                 :            : 
     198                 :            : /**
     199                 :            :  * struct ethnl_dump_ctx - context structure for generic dumpit() callback
     200                 :            :  * @ops:        request ops of currently processed message type
     201                 :            :  * @req_info:   parsed request header of processed request
     202                 :            :  * @reply_data: data needed to compose the reply
     203                 :            :  * @pos_hash:   saved iteration position - hashbucket
     204                 :            :  * @pos_idx:    saved iteration position - index
     205                 :            :  *
     206                 :            :  * These parameters are kept in struct netlink_callback as context preserved
     207                 :            :  * between iterations. They are initialized by ethnl_default_start() and used
     208                 :            :  * in ethnl_default_dumpit() and ethnl_default_done().
     209                 :            :  */
     210                 :            : struct ethnl_dump_ctx {
     211                 :            :         const struct ethnl_request_ops  *ops;
     212                 :            :         struct ethnl_req_info           *req_info;
     213                 :            :         struct ethnl_reply_data         *reply_data;
     214                 :            :         int                             pos_hash;
     215                 :            :         int                             pos_idx;
     216                 :            : };
     217                 :            : 
     218                 :            : static const struct ethnl_request_ops *
     219                 :            : ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
     220                 :            :         [ETHTOOL_MSG_STRSET_GET]        = &ethnl_strset_request_ops,
     221                 :            :         [ETHTOOL_MSG_LINKINFO_GET]      = &ethnl_linkinfo_request_ops,
     222                 :            :         [ETHTOOL_MSG_LINKMODES_GET]     = &ethnl_linkmodes_request_ops,
     223                 :            :         [ETHTOOL_MSG_LINKSTATE_GET]     = &ethnl_linkstate_request_ops,
     224                 :            :         [ETHTOOL_MSG_DEBUG_GET]         = &ethnl_debug_request_ops,
     225                 :            :         [ETHTOOL_MSG_WOL_GET]           = &ethnl_wol_request_ops,
     226                 :            : };
     227                 :            : 
     228                 :          0 : static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
     229                 :            : {
     230                 :          0 :         return (struct ethnl_dump_ctx *)cb->ctx;
     231                 :            : }
     232                 :            : 
     233                 :            : /**
     234                 :            :  * ethnl_default_parse() - Parse request message
     235                 :            :  * @req_info:    pointer to structure to put data into
     236                 :            :  * @nlhdr:       pointer to request message header
     237                 :            :  * @net:         request netns
     238                 :            :  * @request_ops: struct request_ops for request type
     239                 :            :  * @extack:      netlink extack for error reporting
     240                 :            :  * @require_dev: fail if no device identified in header
     241                 :            :  *
     242                 :            :  * Parse universal request header and call request specific ->parse_request()
     243                 :            :  * callback (if defined) to parse the rest of the message.
     244                 :            :  *
     245                 :            :  * Return: 0 on success or negative error code
     246                 :            :  */
     247                 :          0 : static int ethnl_default_parse(struct ethnl_req_info *req_info,
     248                 :            :                                const struct nlmsghdr *nlhdr, struct net *net,
     249                 :            :                                const struct ethnl_request_ops *request_ops,
     250                 :            :                                struct netlink_ext_ack *extack, bool require_dev)
     251                 :            : {
     252                 :          0 :         struct nlattr **tb;
     253                 :          0 :         int ret;
     254                 :            : 
     255                 :          0 :         tb = kmalloc_array(request_ops->max_attr + 1, sizeof(tb[0]),
     256                 :            :                            GFP_KERNEL);
     257         [ #  # ]:          0 :         if (!tb)
     258                 :            :                 return -ENOMEM;
     259                 :            : 
     260                 :          0 :         ret = nlmsg_parse(nlhdr, GENL_HDRLEN, tb, request_ops->max_attr,
     261                 :            :                           request_ops->request_policy, extack);
     262         [ #  # ]:          0 :         if (ret < 0)
     263                 :          0 :                 goto out;
     264                 :          0 :         ret = ethnl_parse_header(req_info, tb[request_ops->hdr_attr], net,
     265                 :            :                                  extack, require_dev);
     266         [ #  # ]:          0 :         if (ret < 0)
     267                 :          0 :                 goto out;
     268                 :            : 
     269         [ #  # ]:          0 :         if (request_ops->parse_request) {
     270                 :          0 :                 ret = request_ops->parse_request(req_info, tb, extack);
     271                 :          0 :                 if (ret < 0)
     272                 :            :                         goto out;
     273                 :            :         }
     274                 :            : 
     275                 :            :         ret = 0;
     276                 :          0 : out:
     277                 :          0 :         kfree(tb);
     278                 :          0 :         return ret;
     279                 :            : }
     280                 :            : 
     281                 :            : /**
     282                 :            :  * ethnl_init_reply_data() - Initialize reply data for GET request
     283                 :            :  * @reply_data: pointer to embedded struct ethnl_reply_data
     284                 :            :  * @ops:        instance of struct ethnl_request_ops describing the layout
     285                 :            :  * @dev:        network device to initialize the reply for
     286                 :            :  *
     287                 :            :  * Fills the reply data part with zeros and sets the dev member. Must be called
     288                 :            :  * before calling the ->fill_reply() callback (for each iteration when handling
     289                 :            :  * dump requests).
     290                 :            :  */
     291                 :          0 : static void ethnl_init_reply_data(struct ethnl_reply_data *reply_data,
     292                 :            :                                   const struct ethnl_request_ops *ops,
     293                 :            :                                   struct net_device *dev)
     294                 :            : {
     295                 :          0 :         memset(reply_data, 0, ops->reply_data_size);
     296                 :          0 :         reply_data->dev = dev;
     297                 :            : }
     298                 :            : 
     299                 :            : /* default ->doit() handler for GET type requests */
     300                 :          0 : static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
     301                 :            : {
     302                 :          0 :         struct ethnl_reply_data *reply_data = NULL;
     303                 :          0 :         struct ethnl_req_info *req_info = NULL;
     304                 :          0 :         const u8 cmd = info->genlhdr->cmd;
     305                 :          0 :         const struct ethnl_request_ops *ops;
     306                 :          0 :         struct sk_buff *rskb;
     307                 :          0 :         void *reply_payload;
     308                 :          0 :         int reply_len;
     309                 :          0 :         int ret;
     310                 :            : 
     311                 :          0 :         ops = ethnl_default_requests[cmd];
     312   [ #  #  #  #  :          0 :         if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd))
                   #  # ]
     313                 :            :                 return -EOPNOTSUPP;
     314                 :          0 :         req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
     315         [ #  # ]:          0 :         if (!req_info)
     316                 :            :                 return -ENOMEM;
     317         [ #  # ]:          0 :         reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
     318         [ #  # ]:          0 :         if (!reply_data) {
     319                 :          0 :                 kfree(req_info);
     320                 :          0 :                 return -ENOMEM;
     321                 :            :         }
     322                 :            : 
     323                 :          0 :         ret = ethnl_default_parse(req_info, info->nlhdr, genl_info_net(info), ops,
     324                 :          0 :                                   info->extack, !ops->allow_nodev_do);
     325         [ #  # ]:          0 :         if (ret < 0)
     326                 :          0 :                 goto err_dev;
     327                 :          0 :         ethnl_init_reply_data(reply_data, ops, req_info->dev);
     328                 :            : 
     329                 :          0 :         rtnl_lock();
     330                 :          0 :         ret = ops->prepare_data(req_info, reply_data, info);
     331                 :          0 :         rtnl_unlock();
     332         [ #  # ]:          0 :         if (ret < 0)
     333                 :          0 :                 goto err_cleanup;
     334                 :          0 :         ret = ops->reply_size(req_info, reply_data);
     335         [ #  # ]:          0 :         if (ret < 0)
     336                 :          0 :                 goto err_cleanup;
     337                 :          0 :         reply_len = ret;
     338                 :          0 :         ret = -ENOMEM;
     339                 :          0 :         rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd,
     340                 :          0 :                                 ops->hdr_attr, info, &reply_payload);
     341         [ #  # ]:          0 :         if (!rskb)
     342                 :          0 :                 goto err_cleanup;
     343                 :          0 :         ret = ops->fill_reply(rskb, req_info, reply_data);
     344         [ #  # ]:          0 :         if (ret < 0)
     345                 :          0 :                 goto err_msg;
     346         [ #  # ]:          0 :         if (ops->cleanup_data)
     347                 :          0 :                 ops->cleanup_data(reply_data);
     348                 :            : 
     349         [ #  # ]:          0 :         genlmsg_end(rskb, reply_payload);
     350         [ #  # ]:          0 :         if (req_info->dev)
     351                 :          0 :                 dev_put(req_info->dev);
     352                 :          0 :         kfree(reply_data);
     353                 :          0 :         kfree(req_info);
     354                 :          0 :         return genlmsg_reply(rskb, info);
     355                 :            : 
     356                 :            : err_msg:
     357   [ #  #  #  # ]:          0 :         WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient\n", reply_len);
     358                 :          0 :         nlmsg_free(rskb);
     359                 :          0 : err_cleanup:
     360         [ #  # ]:          0 :         if (ops->cleanup_data)
     361                 :          0 :                 ops->cleanup_data(reply_data);
     362                 :          0 : err_dev:
     363         [ #  # ]:          0 :         if (req_info->dev)
     364                 :          0 :                 dev_put(req_info->dev);
     365                 :          0 :         kfree(reply_data);
     366                 :          0 :         kfree(req_info);
     367                 :          0 :         return ret;
     368                 :            : }
     369                 :            : 
     370                 :          0 : static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev,
     371                 :            :                                   const struct ethnl_dump_ctx *ctx)
     372                 :            : {
     373                 :          0 :         int ret;
     374                 :            : 
     375                 :          0 :         ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev);
     376                 :          0 :         rtnl_lock();
     377                 :          0 :         ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, NULL);
     378                 :          0 :         rtnl_unlock();
     379         [ #  # ]:          0 :         if (ret < 0)
     380                 :          0 :                 goto out;
     381                 :          0 :         ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr);
     382         [ #  # ]:          0 :         if (ret < 0)
     383                 :          0 :                 goto out;
     384                 :          0 :         ret = ctx->ops->fill_reply(skb, ctx->req_info, ctx->reply_data);
     385                 :            : 
     386                 :          0 : out:
     387         [ #  # ]:          0 :         if (ctx->ops->cleanup_data)
     388                 :          0 :                 ctx->ops->cleanup_data(ctx->reply_data);
     389                 :          0 :         ctx->reply_data->dev = NULL;
     390                 :          0 :         return ret;
     391                 :            : }
     392                 :            : 
     393                 :            : /* Default ->dumpit() handler for GET requests. Device iteration copied from
     394                 :            :  * rtnl_dump_ifinfo(); we have to be more careful about device hashtable
     395                 :            :  * persistence as we cannot guarantee to hold RTNL lock through the whole
     396                 :            :  * function as rtnetnlink does.
     397                 :            :  */
     398                 :          0 : static int ethnl_default_dumpit(struct sk_buff *skb,
     399                 :            :                                 struct netlink_callback *cb)
     400                 :            : {
     401                 :          0 :         struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
     402                 :          0 :         struct net *net = sock_net(skb->sk);
     403                 :          0 :         int s_idx = ctx->pos_idx;
     404                 :          0 :         int h, idx = 0;
     405                 :          0 :         int ret = 0;
     406                 :          0 :         void *ehdr;
     407                 :            : 
     408                 :          0 :         rtnl_lock();
     409         [ #  # ]:          0 :         for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
     410                 :          0 :                 struct hlist_head *head;
     411                 :          0 :                 struct net_device *dev;
     412                 :          0 :                 unsigned int seq;
     413                 :            : 
     414                 :          0 :                 head = &net->dev_index_head[h];
     415                 :            : 
     416                 :          0 : restart_chain:
     417                 :          0 :                 seq = net->dev_base_seq;
     418                 :          0 :                 cb->seq = seq;
     419                 :          0 :                 idx = 0;
     420   [ #  #  #  # ]:          0 :                 hlist_for_each_entry(dev, head, index_hlist) {
     421         [ #  # ]:          0 :                         if (idx < s_idx)
     422                 :          0 :                                 goto cont;
     423                 :          0 :                         dev_hold(dev);
     424                 :          0 :                         rtnl_unlock();
     425                 :            : 
     426                 :          0 :                         ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
     427                 :          0 :                                            cb->nlh->nlmsg_seq,
     428                 :            :                                            &ethtool_genl_family, 0,
     429                 :          0 :                                            ctx->ops->reply_cmd);
     430         [ #  # ]:          0 :                         if (!ehdr) {
     431                 :          0 :                                 dev_put(dev);
     432                 :          0 :                                 ret = -EMSGSIZE;
     433                 :          0 :                                 goto out;
     434                 :            :                         }
     435                 :          0 :                         ret = ethnl_default_dump_one(skb, dev, ctx);
     436                 :          0 :                         dev_put(dev);
     437         [ #  # ]:          0 :                         if (ret < 0) {
     438                 :          0 :                                 genlmsg_cancel(skb, ehdr);
     439         [ #  # ]:          0 :                                 if (ret == -EOPNOTSUPP)
     440                 :          0 :                                         goto lock_and_cont;
     441         [ #  # ]:          0 :                                 if (likely(skb->len))
     442                 :          0 :                                         ret = skb->len;
     443                 :          0 :                                 goto out;
     444                 :            :                         }
     445                 :          0 :                         genlmsg_end(skb, ehdr);
     446                 :          0 : lock_and_cont:
     447                 :          0 :                         rtnl_lock();
     448         [ #  # ]:          0 :                         if (net->dev_base_seq != seq) {
     449                 :          0 :                                 s_idx = idx + 1;
     450                 :          0 :                                 goto restart_chain;
     451                 :            :                         }
     452                 :          0 : cont:
     453         [ #  # ]:          0 :                         idx++;
     454                 :            :                 }
     455                 :            : 
     456                 :            :         }
     457                 :          0 :         rtnl_unlock();
     458                 :            : 
     459                 :          0 : out:
     460                 :          0 :         ctx->pos_hash = h;
     461                 :          0 :         ctx->pos_idx = idx;
     462         [ #  # ]:          0 :         nl_dump_check_consistent(cb, nlmsg_hdr(skb));
     463                 :            : 
     464                 :          0 :         return ret;
     465                 :            : }
     466                 :            : 
     467                 :            : /* generic ->start() handler for GET requests */
     468                 :          0 : static int ethnl_default_start(struct netlink_callback *cb)
     469                 :            : {
     470                 :          0 :         struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
     471                 :          0 :         struct ethnl_reply_data *reply_data;
     472                 :          0 :         const struct ethnl_request_ops *ops;
     473                 :          0 :         struct ethnl_req_info *req_info;
     474                 :          0 :         struct genlmsghdr *ghdr;
     475                 :          0 :         int ret;
     476                 :            : 
     477                 :          0 :         BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
     478                 :            : 
     479         [ #  # ]:          0 :         ghdr = nlmsg_data(cb->nlh);
     480                 :          0 :         ops = ethnl_default_requests[ghdr->cmd];
     481   [ #  #  #  #  :          0 :         if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd))
                   #  # ]
     482                 :            :                 return -EOPNOTSUPP;
     483                 :          0 :         req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
     484         [ #  # ]:          0 :         if (!req_info)
     485                 :            :                 return -ENOMEM;
     486         [ #  # ]:          0 :         reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
     487         [ #  # ]:          0 :         if (!reply_data) {
     488                 :          0 :                 ret = -ENOMEM;
     489                 :          0 :                 goto free_req_info;
     490                 :            :         }
     491                 :            : 
     492                 :          0 :         ret = ethnl_default_parse(req_info, cb->nlh, sock_net(cb->skb->sk), ops,
     493                 :            :                                   cb->extack, false);
     494         [ #  # ]:          0 :         if (req_info->dev) {
     495                 :            :                 /* We ignore device specification in dump requests but as the
     496                 :            :                  * same parser as for non-dump (doit) requests is used, it
     497                 :            :                  * would take reference to the device if it finds one
     498                 :            :                  */
     499                 :          0 :                 dev_put(req_info->dev);
     500                 :          0 :                 req_info->dev = NULL;
     501                 :            :         }
     502         [ #  # ]:          0 :         if (ret < 0)
     503                 :          0 :                 goto free_reply_data;
     504                 :            : 
     505                 :          0 :         ctx->ops = ops;
     506                 :          0 :         ctx->req_info = req_info;
     507                 :          0 :         ctx->reply_data = reply_data;
     508                 :          0 :         ctx->pos_hash = 0;
     509                 :          0 :         ctx->pos_idx = 0;
     510                 :            : 
     511                 :          0 :         return 0;
     512                 :            : 
     513                 :            : free_reply_data:
     514                 :          0 :         kfree(reply_data);
     515                 :          0 : free_req_info:
     516                 :          0 :         kfree(req_info);
     517                 :            : 
     518                 :          0 :         return ret;
     519                 :            : }
     520                 :            : 
     521                 :            : /* default ->done() handler for GET requests */
     522                 :          0 : static int ethnl_default_done(struct netlink_callback *cb)
     523                 :            : {
     524                 :          0 :         struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
     525                 :            : 
     526                 :          0 :         kfree(ctx->reply_data);
     527                 :          0 :         kfree(ctx->req_info);
     528                 :            : 
     529                 :          0 :         return 0;
     530                 :            : }
     531                 :            : 
     532                 :            : static const struct ethnl_request_ops *
     533                 :            : ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
     534                 :            :         [ETHTOOL_MSG_LINKINFO_NTF]      = &ethnl_linkinfo_request_ops,
     535                 :            :         [ETHTOOL_MSG_LINKMODES_NTF]     = &ethnl_linkmodes_request_ops,
     536                 :            :         [ETHTOOL_MSG_DEBUG_NTF]         = &ethnl_debug_request_ops,
     537                 :            :         [ETHTOOL_MSG_WOL_NTF]           = &ethnl_wol_request_ops,
     538                 :            : };
     539                 :            : 
     540                 :            : /* default notification handler */
     541                 :          0 : static void ethnl_default_notify(struct net_device *dev, unsigned int cmd,
     542                 :            :                                  const void *data)
     543                 :            : {
     544                 :          0 :         struct ethnl_reply_data *reply_data;
     545                 :          0 :         const struct ethnl_request_ops *ops;
     546                 :          0 :         struct ethnl_req_info *req_info;
     547                 :          0 :         struct sk_buff *skb;
     548                 :          0 :         void *reply_payload;
     549                 :          0 :         int reply_len;
     550                 :          0 :         int ret;
     551                 :            : 
     552   [ #  #  #  #  :          0 :         if (WARN_ONCE(cmd > ETHTOOL_MSG_KERNEL_MAX ||
          #  #  #  #  #  
                      # ]
     553                 :            :                       !ethnl_default_notify_ops[cmd],
     554                 :            :                       "unexpected notification type %u\n", cmd))
     555                 :            :                 return;
     556                 :          0 :         ops = ethnl_default_notify_ops[cmd];
     557                 :          0 :         req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
     558         [ #  # ]:          0 :         if (!req_info)
     559                 :            :                 return;
     560         [ #  # ]:          0 :         reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
     561         [ #  # ]:          0 :         if (!reply_data) {
     562                 :          0 :                 kfree(req_info);
     563                 :          0 :                 return;
     564                 :            :         }
     565                 :            : 
     566                 :          0 :         req_info->dev = dev;
     567                 :          0 :         req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS;
     568                 :            : 
     569                 :          0 :         ethnl_init_reply_data(reply_data, ops, dev);
     570                 :          0 :         ret = ops->prepare_data(req_info, reply_data, NULL);
     571         [ #  # ]:          0 :         if (ret < 0)
     572                 :          0 :                 goto err_cleanup;
     573                 :          0 :         ret = ops->reply_size(req_info, reply_data);
     574         [ #  # ]:          0 :         if (ret < 0)
     575                 :          0 :                 goto err_cleanup;
     576                 :          0 :         reply_len = ret;
     577                 :          0 :         ret = -ENOMEM;
     578                 :          0 :         skb = genlmsg_new(reply_len, GFP_KERNEL);
     579         [ #  # ]:          0 :         if (!skb)
     580                 :          0 :                 goto err_cleanup;
     581                 :          0 :         reply_payload = ethnl_bcastmsg_put(skb, cmd);
     582         [ #  # ]:          0 :         if (!reply_payload)
     583                 :          0 :                 goto err_skb;
     584                 :          0 :         ret = ethnl_fill_reply_header(skb, dev, ops->hdr_attr);
     585         [ #  # ]:          0 :         if (ret < 0)
     586                 :          0 :                 goto err_msg;
     587                 :          0 :         ret = ops->fill_reply(skb, req_info, reply_data);
     588         [ #  # ]:          0 :         if (ret < 0)
     589                 :          0 :                 goto err_msg;
     590         [ #  # ]:          0 :         if (ops->cleanup_data)
     591                 :          0 :                 ops->cleanup_data(reply_data);
     592                 :            : 
     593                 :          0 :         genlmsg_end(skb, reply_payload);
     594                 :          0 :         kfree(reply_data);
     595                 :          0 :         kfree(req_info);
     596                 :          0 :         ethnl_multicast(skb, dev);
     597                 :          0 :         return;
     598                 :            : 
     599                 :          0 : err_msg:
     600   [ #  #  #  # ]:          0 :         WARN_ONCE(ret == -EMSGSIZE,
     601                 :            :                   "calculated message payload length (%d) not sufficient\n",
     602                 :            :                   reply_len);
     603                 :          0 : err_skb:
     604                 :          0 :         nlmsg_free(skb);
     605                 :          0 : err_cleanup:
     606         [ #  # ]:          0 :         if (ops->cleanup_data)
     607                 :          0 :                 ops->cleanup_data(reply_data);
     608                 :          0 :         kfree(reply_data);
     609                 :          0 :         kfree(req_info);
     610                 :          0 :         return;
     611                 :            : }
     612                 :            : 
     613                 :            : /* notifications */
     614                 :            : 
     615                 :            : typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd,
     616                 :            :                                        const void *data);
     617                 :            : 
     618                 :            : static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
     619                 :            :         [ETHTOOL_MSG_LINKINFO_NTF]      = ethnl_default_notify,
     620                 :            :         [ETHTOOL_MSG_LINKMODES_NTF]     = ethnl_default_notify,
     621                 :            :         [ETHTOOL_MSG_DEBUG_NTF]         = ethnl_default_notify,
     622                 :            :         [ETHTOOL_MSG_WOL_NTF]           = ethnl_default_notify,
     623                 :            : };
     624                 :            : 
     625                 :          0 : void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
     626                 :            : {
     627         [ #  # ]:          0 :         if (unlikely(!ethnl_ok))
     628                 :            :                 return;
     629   [ #  #  #  # ]:          0 :         ASSERT_RTNL();
     630                 :            : 
     631   [ #  #  #  # ]:          0 :         if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
     632                 :            :                    ethnl_notify_handlers[cmd]))
     633                 :          0 :                 ethnl_notify_handlers[cmd](dev, cmd, data);
     634                 :            :         else
     635   [ #  #  #  # ]:          0 :                 WARN_ONCE(1, "notification %u not implemented (dev=%s)\n",
     636                 :            :                           cmd, netdev_name(dev));
     637                 :            : }
     638                 :            : EXPORT_SYMBOL(ethtool_notify);
     639                 :            : 
     640                 :            : /* genetlink setup */
     641                 :            : 
     642                 :            : static const struct genl_ops ethtool_genl_ops[] = {
     643                 :            :         {
     644                 :            :                 .cmd    = ETHTOOL_MSG_STRSET_GET,
     645                 :            :                 .doit   = ethnl_default_doit,
     646                 :            :                 .start  = ethnl_default_start,
     647                 :            :                 .dumpit = ethnl_default_dumpit,
     648                 :            :                 .done   = ethnl_default_done,
     649                 :            :         },
     650                 :            :         {
     651                 :            :                 .cmd    = ETHTOOL_MSG_LINKINFO_GET,
     652                 :            :                 .doit   = ethnl_default_doit,
     653                 :            :                 .start  = ethnl_default_start,
     654                 :            :                 .dumpit = ethnl_default_dumpit,
     655                 :            :                 .done   = ethnl_default_done,
     656                 :            :         },
     657                 :            :         {
     658                 :            :                 .cmd    = ETHTOOL_MSG_LINKINFO_SET,
     659                 :            :                 .flags  = GENL_UNS_ADMIN_PERM,
     660                 :            :                 .doit   = ethnl_set_linkinfo,
     661                 :            :         },
     662                 :            :         {
     663                 :            :                 .cmd    = ETHTOOL_MSG_LINKMODES_GET,
     664                 :            :                 .doit   = ethnl_default_doit,
     665                 :            :                 .start  = ethnl_default_start,
     666                 :            :                 .dumpit = ethnl_default_dumpit,
     667                 :            :                 .done   = ethnl_default_done,
     668                 :            :         },
     669                 :            :         {
     670                 :            :                 .cmd    = ETHTOOL_MSG_LINKMODES_SET,
     671                 :            :                 .flags  = GENL_UNS_ADMIN_PERM,
     672                 :            :                 .doit   = ethnl_set_linkmodes,
     673                 :            :         },
     674                 :            :         {
     675                 :            :                 .cmd    = ETHTOOL_MSG_LINKSTATE_GET,
     676                 :            :                 .doit   = ethnl_default_doit,
     677                 :            :                 .start  = ethnl_default_start,
     678                 :            :                 .dumpit = ethnl_default_dumpit,
     679                 :            :                 .done   = ethnl_default_done,
     680                 :            :         },
     681                 :            :         {
     682                 :            :                 .cmd    = ETHTOOL_MSG_DEBUG_GET,
     683                 :            :                 .doit   = ethnl_default_doit,
     684                 :            :                 .start  = ethnl_default_start,
     685                 :            :                 .dumpit = ethnl_default_dumpit,
     686                 :            :                 .done   = ethnl_default_done,
     687                 :            :         },
     688                 :            :         {
     689                 :            :                 .cmd    = ETHTOOL_MSG_DEBUG_SET,
     690                 :            :                 .flags  = GENL_UNS_ADMIN_PERM,
     691                 :            :                 .doit   = ethnl_set_debug,
     692                 :            :         },
     693                 :            :         {
     694                 :            :                 .cmd    = ETHTOOL_MSG_WOL_GET,
     695                 :            :                 .flags  = GENL_UNS_ADMIN_PERM,
     696                 :            :                 .doit   = ethnl_default_doit,
     697                 :            :                 .start  = ethnl_default_start,
     698                 :            :                 .dumpit = ethnl_default_dumpit,
     699                 :            :                 .done   = ethnl_default_done,
     700                 :            :         },
     701                 :            :         {
     702                 :            :                 .cmd    = ETHTOOL_MSG_WOL_SET,
     703                 :            :                 .flags  = GENL_UNS_ADMIN_PERM,
     704                 :            :                 .doit   = ethnl_set_wol,
     705                 :            :         },
     706                 :            : };
     707                 :            : 
     708                 :            : static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
     709                 :            :         [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
     710                 :            : };
     711                 :            : 
     712                 :            : static struct genl_family ethtool_genl_family = {
     713                 :            :         .name           = ETHTOOL_GENL_NAME,
     714                 :            :         .version        = ETHTOOL_GENL_VERSION,
     715                 :            :         .netnsok        = true,
     716                 :            :         .parallel_ops   = true,
     717                 :            :         .ops            = ethtool_genl_ops,
     718                 :            :         .n_ops          = ARRAY_SIZE(ethtool_genl_ops),
     719                 :            :         .mcgrps         = ethtool_nl_mcgrps,
     720                 :            :         .n_mcgrps       = ARRAY_SIZE(ethtool_nl_mcgrps),
     721                 :            : };
     722                 :            : 
     723                 :            : /* module setup */
     724                 :            : 
     725                 :          3 : static int __init ethnl_init(void)
     726                 :            : {
     727                 :          3 :         int ret;
     728                 :            : 
     729                 :          3 :         ret = genl_register_family(&ethtool_genl_family);
     730   [ -  +  +  - ]:          3 :         if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
     731                 :            :                 return ret;
     732                 :          3 :         ethnl_ok = true;
     733                 :            : 
     734                 :          3 :         return 0;
     735                 :            : }
     736                 :            : 
     737                 :            : subsys_initcall(ethnl_init);

Generated by: LCOV version 1.14