1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 #include "bitset.h" 6 7 struct privflags_req_info { 8 struct ethnl_req_info base; 9 }; 10 11 struct privflags_reply_data { 12 struct ethnl_reply_data base; 13 const char (*priv_flag_names)[ETH_GSTRING_LEN]; 14 unsigned int n_priv_flags; 15 u32 priv_flags; 16 }; 17 18 #define PRIVFLAGS_REPDATA(__reply_base) \ 19 container_of(__reply_base, struct privflags_reply_data, base) 20 21 const struct nla_policy ethnl_privflags_get_policy[] = { 22 [ETHTOOL_A_PRIVFLAGS_HEADER] = 23 NLA_POLICY_NESTED(ethnl_header_policy), 24 }; 25 26 static int ethnl_get_priv_flags_info(struct net_device *dev, 27 unsigned int *count, 28 const char (**names)[ETH_GSTRING_LEN]) 29 { 30 const struct ethtool_ops *ops = dev->ethtool_ops; 31 int nflags; 32 33 nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS); 34 if (nflags < 0) 35 return nflags; 36 37 if (names) { 38 *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL); 39 if (!*names) 40 return -ENOMEM; 41 ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names); 42 } 43 44 /* We can pass more than 32 private flags to userspace via netlink but 45 * we cannot get more with ethtool_ops::get_priv_flags(). Note that we 46 * must not adjust nflags before allocating the space for flag names 47 * as the buffer must be large enough for all flags. 48 */ 49 if (WARN_ONCE(nflags > 32, 50 "device %s reports more than 32 private flags (%d)\n", 51 netdev_name(dev), nflags)) 52 nflags = 32; 53 *count = nflags; 54 55 return 0; 56 } 57 58 static int privflags_prepare_data(const struct ethnl_req_info *req_base, 59 struct ethnl_reply_data *reply_base, 60 struct genl_info *info) 61 { 62 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); 63 struct net_device *dev = reply_base->dev; 64 const char (*names)[ETH_GSTRING_LEN]; 65 const struct ethtool_ops *ops; 66 unsigned int nflags; 67 int ret; 68 69 ops = dev->ethtool_ops; 70 if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings) 71 return -EOPNOTSUPP; 72 ret = ethnl_ops_begin(dev); 73 if (ret < 0) 74 return ret; 75 76 ret = ethnl_get_priv_flags_info(dev, &nflags, &names); 77 if (ret < 0) 78 goto out_ops; 79 data->priv_flags = ops->get_priv_flags(dev); 80 data->priv_flag_names = names; 81 data->n_priv_flags = nflags; 82 83 out_ops: 84 ethnl_ops_complete(dev); 85 return ret; 86 } 87 88 static int privflags_reply_size(const struct ethnl_req_info *req_base, 89 const struct ethnl_reply_data *reply_base) 90 { 91 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); 92 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 93 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags); 94 95 return ethnl_bitset32_size(&data->priv_flags, &all_flags, 96 data->n_priv_flags, 97 data->priv_flag_names, compact); 98 } 99 100 static int privflags_fill_reply(struct sk_buff *skb, 101 const struct ethnl_req_info *req_base, 102 const struct ethnl_reply_data *reply_base) 103 { 104 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); 105 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 106 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags); 107 108 return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS, 109 &data->priv_flags, &all_flags, 110 data->n_priv_flags, data->priv_flag_names, 111 compact); 112 } 113 114 static void privflags_cleanup_data(struct ethnl_reply_data *reply_data) 115 { 116 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data); 117 118 kfree(data->priv_flag_names); 119 } 120 121 const struct ethnl_request_ops ethnl_privflags_request_ops = { 122 .request_cmd = ETHTOOL_MSG_PRIVFLAGS_GET, 123 .reply_cmd = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, 124 .hdr_attr = ETHTOOL_A_PRIVFLAGS_HEADER, 125 .req_info_size = sizeof(struct privflags_req_info), 126 .reply_data_size = sizeof(struct privflags_reply_data), 127 128 .prepare_data = privflags_prepare_data, 129 .reply_size = privflags_reply_size, 130 .fill_reply = privflags_fill_reply, 131 .cleanup_data = privflags_cleanup_data, 132 }; 133 134 /* PRIVFLAGS_SET */ 135 136 const struct nla_policy ethnl_privflags_set_policy[] = { 137 [ETHTOOL_A_PRIVFLAGS_HEADER] = 138 NLA_POLICY_NESTED(ethnl_header_policy), 139 [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_NESTED }, 140 }; 141 142 int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info) 143 { 144 const char (*names)[ETH_GSTRING_LEN] = NULL; 145 struct ethnl_req_info req_info = {}; 146 struct nlattr **tb = info->attrs; 147 const struct ethtool_ops *ops; 148 struct net_device *dev; 149 unsigned int nflags; 150 bool mod = false; 151 bool compact; 152 u32 flags; 153 int ret; 154 155 if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS]) 156 return -EINVAL; 157 ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact); 158 if (ret < 0) 159 return ret; 160 ret = ethnl_parse_header_dev_get(&req_info, 161 tb[ETHTOOL_A_PRIVFLAGS_HEADER], 162 genl_info_net(info), info->extack, 163 true); 164 if (ret < 0) 165 return ret; 166 dev = req_info.dev; 167 ops = dev->ethtool_ops; 168 ret = -EOPNOTSUPP; 169 if (!ops->get_priv_flags || !ops->set_priv_flags || 170 !ops->get_sset_count || !ops->get_strings) 171 goto out_dev; 172 173 rtnl_lock(); 174 ret = ethnl_ops_begin(dev); 175 if (ret < 0) 176 goto out_rtnl; 177 ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names); 178 if (ret < 0) 179 goto out_ops; 180 flags = ops->get_priv_flags(dev); 181 182 ret = ethnl_update_bitset32(&flags, nflags, 183 tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names, 184 info->extack, &mod); 185 if (ret < 0 || !mod) 186 goto out_free; 187 ret = ops->set_priv_flags(dev, flags); 188 if (ret < 0) 189 goto out_free; 190 ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL); 191 192 out_free: 193 kfree(names); 194 out_ops: 195 ethnl_ops_complete(dev); 196 out_rtnl: 197 rtnl_unlock(); 198 out_dev: 199 ethnl_parse_header_dev_put(&req_info); 200 return ret; 201 } 202