1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/ethtool.h> 4 #include <linux/phy.h> 5 #include "netlink.h" 6 #include "common.h" 7 8 struct strset_info { 9 bool per_dev; 10 bool free_strings; 11 unsigned int count; 12 const char (*strings)[ETH_GSTRING_LEN]; 13 }; 14 15 static const struct strset_info info_template[] = { 16 [ETH_SS_TEST] = { 17 .per_dev = true, 18 }, 19 [ETH_SS_STATS] = { 20 .per_dev = true, 21 }, 22 [ETH_SS_PRIV_FLAGS] = { 23 .per_dev = true, 24 }, 25 [ETH_SS_FEATURES] = { 26 .per_dev = false, 27 .count = ARRAY_SIZE(netdev_features_strings), 28 .strings = netdev_features_strings, 29 }, 30 [ETH_SS_RSS_HASH_FUNCS] = { 31 .per_dev = false, 32 .count = ARRAY_SIZE(rss_hash_func_strings), 33 .strings = rss_hash_func_strings, 34 }, 35 [ETH_SS_TUNABLES] = { 36 .per_dev = false, 37 .count = ARRAY_SIZE(tunable_strings), 38 .strings = tunable_strings, 39 }, 40 [ETH_SS_PHY_STATS] = { 41 .per_dev = true, 42 }, 43 [ETH_SS_PHY_TUNABLES] = { 44 .per_dev = false, 45 .count = ARRAY_SIZE(phy_tunable_strings), 46 .strings = phy_tunable_strings, 47 }, 48 [ETH_SS_LINK_MODES] = { 49 .per_dev = false, 50 .count = __ETHTOOL_LINK_MODE_MASK_NBITS, 51 .strings = link_mode_names, 52 }, 53 [ETH_SS_MSG_CLASSES] = { 54 .per_dev = false, 55 .count = NETIF_MSG_CLASS_COUNT, 56 .strings = netif_msg_class_names, 57 }, 58 [ETH_SS_WOL_MODES] = { 59 .per_dev = false, 60 .count = WOL_MODE_COUNT, 61 .strings = wol_mode_names, 62 }, 63 [ETH_SS_SOF_TIMESTAMPING] = { 64 .per_dev = false, 65 .count = __SOF_TIMESTAMPING_CNT, 66 .strings = sof_timestamping_names, 67 }, 68 [ETH_SS_TS_TX_TYPES] = { 69 .per_dev = false, 70 .count = __HWTSTAMP_TX_CNT, 71 .strings = ts_tx_type_names, 72 }, 73 [ETH_SS_TS_RX_FILTERS] = { 74 .per_dev = false, 75 .count = __HWTSTAMP_FILTER_CNT, 76 .strings = ts_rx_filter_names, 77 }, 78 [ETH_SS_UDP_TUNNEL_TYPES] = { 79 .per_dev = false, 80 .count = __ETHTOOL_UDP_TUNNEL_TYPE_CNT, 81 .strings = udp_tunnel_type_names, 82 }, 83 [ETH_SS_STATS_STD] = { 84 .per_dev = false, 85 .count = __ETHTOOL_STATS_CNT, 86 .strings = stats_std_names, 87 }, 88 [ETH_SS_STATS_ETH_PHY] = { 89 .per_dev = false, 90 .count = __ETHTOOL_A_STATS_ETH_PHY_CNT, 91 .strings = stats_eth_phy_names, 92 }, 93 [ETH_SS_STATS_ETH_MAC] = { 94 .per_dev = false, 95 .count = __ETHTOOL_A_STATS_ETH_MAC_CNT, 96 .strings = stats_eth_mac_names, 97 }, 98 [ETH_SS_STATS_ETH_CTRL] = { 99 .per_dev = false, 100 .count = __ETHTOOL_A_STATS_ETH_CTRL_CNT, 101 .strings = stats_eth_ctrl_names, 102 }, 103 [ETH_SS_STATS_RMON] = { 104 .per_dev = false, 105 .count = __ETHTOOL_A_STATS_RMON_CNT, 106 .strings = stats_rmon_names, 107 }, 108 }; 109 110 struct strset_req_info { 111 struct ethnl_req_info base; 112 u32 req_ids; 113 bool counts_only; 114 }; 115 116 #define STRSET_REQINFO(__req_base) \ 117 container_of(__req_base, struct strset_req_info, base) 118 119 struct strset_reply_data { 120 struct ethnl_reply_data base; 121 struct strset_info sets[ETH_SS_COUNT]; 122 }; 123 124 #define STRSET_REPDATA(__reply_base) \ 125 container_of(__reply_base, struct strset_reply_data, base) 126 127 const struct nla_policy ethnl_strset_get_policy[] = { 128 [ETHTOOL_A_STRSET_HEADER] = 129 NLA_POLICY_NESTED(ethnl_header_policy), 130 [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED }, 131 [ETHTOOL_A_STRSET_COUNTS_ONLY] = { .type = NLA_FLAG }, 132 }; 133 134 static const struct nla_policy get_stringset_policy[] = { 135 [ETHTOOL_A_STRINGSET_ID] = { .type = NLA_U32 }, 136 }; 137 138 /** 139 * strset_include() - test if a string set should be included in reply 140 * @info: parsed client request 141 * @data: pointer to request data structure 142 * @id: id of string set to check (ETH_SS_* constants) 143 */ 144 static bool strset_include(const struct strset_req_info *info, 145 const struct strset_reply_data *data, u32 id) 146 { 147 bool per_dev; 148 149 BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids)); 150 151 if (info->req_ids) 152 return info->req_ids & (1U << id); 153 per_dev = data->sets[id].per_dev; 154 if (!per_dev && !data->sets[id].strings) 155 return false; 156 157 return data->base.dev ? per_dev : !per_dev; 158 } 159 160 static int strset_get_id(const struct nlattr *nest, u32 *val, 161 struct netlink_ext_ack *extack) 162 { 163 struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)]; 164 int ret; 165 166 ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest, 167 get_stringset_policy, extack); 168 if (ret < 0) 169 return ret; 170 if (NL_REQ_ATTR_CHECK(extack, nest, tb, ETHTOOL_A_STRINGSET_ID)) 171 return -EINVAL; 172 173 *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]); 174 return 0; 175 } 176 177 static const struct nla_policy strset_stringsets_policy[] = { 178 [ETHTOOL_A_STRINGSETS_STRINGSET] = { .type = NLA_NESTED }, 179 }; 180 181 static int strset_parse_request(struct ethnl_req_info *req_base, 182 struct nlattr **tb, 183 struct netlink_ext_ack *extack) 184 { 185 struct strset_req_info *req_info = STRSET_REQINFO(req_base); 186 struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS]; 187 struct nlattr *attr; 188 int rem, ret; 189 190 if (!nest) 191 return 0; 192 ret = nla_validate_nested(nest, 193 ARRAY_SIZE(strset_stringsets_policy) - 1, 194 strset_stringsets_policy, extack); 195 if (ret < 0) 196 return ret; 197 198 req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY]; 199 nla_for_each_nested(attr, nest, rem) { 200 u32 id; 201 202 if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET, 203 "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n", 204 nla_type(attr))) 205 return -EINVAL; 206 207 ret = strset_get_id(attr, &id, extack); 208 if (ret < 0) 209 return ret; 210 if (id >= ETH_SS_COUNT) { 211 NL_SET_ERR_MSG_ATTR(extack, attr, 212 "unknown string set id"); 213 return -EOPNOTSUPP; 214 } 215 216 req_info->req_ids |= (1U << id); 217 } 218 219 return 0; 220 } 221 222 static void strset_cleanup_data(struct ethnl_reply_data *reply_base) 223 { 224 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 225 unsigned int i; 226 227 for (i = 0; i < ETH_SS_COUNT; i++) 228 if (data->sets[i].free_strings) { 229 kfree(data->sets[i].strings); 230 data->sets[i].strings = NULL; 231 data->sets[i].free_strings = false; 232 } 233 } 234 235 static int strset_prepare_set(struct strset_info *info, struct net_device *dev, 236 unsigned int id, bool counts_only) 237 { 238 const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops; 239 const struct ethtool_ops *ops = dev->ethtool_ops; 240 void *strings; 241 int count, ret; 242 243 if (id == ETH_SS_PHY_STATS && dev->phydev && 244 !ops->get_ethtool_phy_stats && phy_ops && 245 phy_ops->get_sset_count) 246 ret = phy_ops->get_sset_count(dev->phydev); 247 else if (ops->get_sset_count && ops->get_strings) 248 ret = ops->get_sset_count(dev, id); 249 else 250 ret = -EOPNOTSUPP; 251 if (ret <= 0) { 252 info->count = 0; 253 return 0; 254 } 255 256 count = ret; 257 if (!counts_only) { 258 strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL); 259 if (!strings) 260 return -ENOMEM; 261 if (id == ETH_SS_PHY_STATS && dev->phydev && 262 !ops->get_ethtool_phy_stats && phy_ops && 263 phy_ops->get_strings) 264 phy_ops->get_strings(dev->phydev, strings); 265 else 266 ops->get_strings(dev, id, strings); 267 info->strings = strings; 268 info->free_strings = true; 269 } 270 info->count = count; 271 272 return 0; 273 } 274 275 static int strset_prepare_data(const struct ethnl_req_info *req_base, 276 struct ethnl_reply_data *reply_base, 277 const struct genl_info *info) 278 { 279 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 280 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 281 struct net_device *dev = reply_base->dev; 282 unsigned int i; 283 int ret; 284 285 BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT); 286 memcpy(&data->sets, &info_template, sizeof(data->sets)); 287 288 if (!dev) { 289 for (i = 0; i < ETH_SS_COUNT; i++) { 290 if ((req_info->req_ids & (1U << i)) && 291 data->sets[i].per_dev) { 292 if (info) 293 GENL_SET_ERR_MSG(info, "requested per device strings without dev"); 294 return -EINVAL; 295 } 296 } 297 return 0; 298 } 299 300 ret = ethnl_ops_begin(dev); 301 if (ret < 0) 302 goto err_strset; 303 for (i = 0; i < ETH_SS_COUNT; i++) { 304 if (!strset_include(req_info, data, i) || 305 !data->sets[i].per_dev) 306 continue; 307 308 ret = strset_prepare_set(&data->sets[i], dev, i, 309 req_info->counts_only); 310 if (ret < 0) 311 goto err_ops; 312 } 313 ethnl_ops_complete(dev); 314 315 return 0; 316 err_ops: 317 ethnl_ops_complete(dev); 318 err_strset: 319 strset_cleanup_data(reply_base); 320 return ret; 321 } 322 323 /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */ 324 static int strset_set_size(const struct strset_info *info, bool counts_only) 325 { 326 unsigned int len = 0; 327 unsigned int i; 328 329 if (info->count == 0) 330 return 0; 331 if (counts_only) 332 return nla_total_size(2 * nla_total_size(sizeof(u32))); 333 334 for (i = 0; i < info->count; i++) { 335 const char *str = info->strings[i]; 336 337 /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */ 338 len += nla_total_size(nla_total_size(sizeof(u32)) + 339 ethnl_strz_size(str)); 340 } 341 /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */ 342 len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len); 343 344 return nla_total_size(len); 345 } 346 347 static int strset_reply_size(const struct ethnl_req_info *req_base, 348 const struct ethnl_reply_data *reply_base) 349 { 350 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 351 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 352 unsigned int i; 353 int len = 0; 354 int ret; 355 356 len += nla_total_size(0); /* ETHTOOL_A_STRSET_STRINGSETS */ 357 358 for (i = 0; i < ETH_SS_COUNT; i++) { 359 const struct strset_info *set_info = &data->sets[i]; 360 361 if (!strset_include(req_info, data, i)) 362 continue; 363 364 ret = strset_set_size(set_info, req_info->counts_only); 365 if (ret < 0) 366 return ret; 367 len += ret; 368 } 369 370 return len; 371 } 372 373 /* fill one string into reply */ 374 static int strset_fill_string(struct sk_buff *skb, 375 const struct strset_info *set_info, u32 idx) 376 { 377 struct nlattr *string_attr; 378 const char *value; 379 380 value = set_info->strings[idx]; 381 382 string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING); 383 if (!string_attr) 384 return -EMSGSIZE; 385 if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) || 386 ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value)) 387 goto nla_put_failure; 388 nla_nest_end(skb, string_attr); 389 390 return 0; 391 nla_put_failure: 392 nla_nest_cancel(skb, string_attr); 393 return -EMSGSIZE; 394 } 395 396 /* fill one string set into reply */ 397 static int strset_fill_set(struct sk_buff *skb, 398 const struct strset_info *set_info, u32 id, 399 bool counts_only) 400 { 401 struct nlattr *stringset_attr; 402 struct nlattr *strings_attr; 403 unsigned int i; 404 405 if (!set_info->per_dev && !set_info->strings) 406 return -EOPNOTSUPP; 407 if (set_info->count == 0) 408 return 0; 409 stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET); 410 if (!stringset_attr) 411 return -EMSGSIZE; 412 413 if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) || 414 nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count)) 415 goto nla_put_failure; 416 417 if (!counts_only) { 418 strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS); 419 if (!strings_attr) 420 goto nla_put_failure; 421 for (i = 0; i < set_info->count; i++) { 422 if (strset_fill_string(skb, set_info, i) < 0) 423 goto nla_put_failure; 424 } 425 nla_nest_end(skb, strings_attr); 426 } 427 428 nla_nest_end(skb, stringset_attr); 429 return 0; 430 431 nla_put_failure: 432 nla_nest_cancel(skb, stringset_attr); 433 return -EMSGSIZE; 434 } 435 436 static int strset_fill_reply(struct sk_buff *skb, 437 const struct ethnl_req_info *req_base, 438 const struct ethnl_reply_data *reply_base) 439 { 440 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 441 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 442 struct nlattr *nest; 443 unsigned int i; 444 int ret; 445 446 nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS); 447 if (!nest) 448 return -EMSGSIZE; 449 450 for (i = 0; i < ETH_SS_COUNT; i++) { 451 if (strset_include(req_info, data, i)) { 452 ret = strset_fill_set(skb, &data->sets[i], i, 453 req_info->counts_only); 454 if (ret < 0) 455 goto nla_put_failure; 456 } 457 } 458 459 nla_nest_end(skb, nest); 460 return 0; 461 462 nla_put_failure: 463 nla_nest_cancel(skb, nest); 464 return ret; 465 } 466 467 const struct ethnl_request_ops ethnl_strset_request_ops = { 468 .request_cmd = ETHTOOL_MSG_STRSET_GET, 469 .reply_cmd = ETHTOOL_MSG_STRSET_GET_REPLY, 470 .hdr_attr = ETHTOOL_A_STRSET_HEADER, 471 .req_info_size = sizeof(struct strset_req_info), 472 .reply_data_size = sizeof(struct strset_reply_data), 473 .allow_nodev_do = true, 474 475 .parse_request = strset_parse_request, 476 .prepare_data = strset_prepare_data, 477 .reply_size = strset_reply_size, 478 .fill_reply = strset_fill_reply, 479 .cleanup_data = strset_cleanup_data, 480 }; 481
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.