Implement WOL_GET request to get wake-on-lan settings for a device, traditionally available via ETHTOOL_GWOL ioctl request. As part of the implementation, provide symbolic names for wake-on-line modes as ETH_SS_WOL_MODES string set.
Unified · Split
| @@ -187,6 +187,7 @@ Userspace to kernel: · 186 unmodified lines | ||
| 187 | 187 | ``ETHTOOL_MSG_LINKSTATE_GET`` get link state |
| 188 | 188 | ``ETHTOOL_MSG_DEBUG_GET`` get debugging settings |
| 189 | 189 | ``ETHTOOL_MSG_DEBUG_SET`` set debugging settings |
| 190 | + ``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings | |
| 190 | 191 | ===================================== ================================ |
| 191 | 192 | |
| 192 | 193 | Kernel to userspace: |
| @@ -200,6 +201,7 @@ Kernel to userspace: · 7 unmodified lines | ||
| 200 | 201 | ``ETHTOOL_MSG_LINKSTATE_GET_REPLY`` link state info |
| 201 | 202 | ``ETHTOOL_MSG_DEBUG_GET_REPLY`` debugging settings |
| 202 | 203 | ``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification |
| 204 | + ``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings | |
| 203 | 205 | ===================================== ================================ |
| 204 | 206 | |
| 205 | 207 | ``GET`` requests are sent by userspace applications to retrieve device |
| @@ -474,6 +476,32 @@ Request contents: · 268 unmodified lines | ||
| 474 | 476 | enabled debugging message types for the device. |
| 475 | 477 | |
| 476 | 478 | |
| 479 | +WOL_GET | |
| 480 | +======= | |
| 481 | + | |
| 482 | +Query device wake-on-lan settings. Unlike most "GET" type requests, | |
| 483 | +``ETHTOOL_MSG_WOL_GET`` requires (netns) ``CAP_NET_ADMIN`` privileges as it | |
| 484 | +(potentially) provides SecureOn(tm) password which is confidential. | |
| 485 | + | |
| 486 | +Request contents: | |
| 487 | + | |
| 488 | + ==================================== ====== ========================== | |
| 489 | + ``ETHTOOL_A_WOL_HEADER`` nested request header | |
| 490 | + ==================================== ====== ========================== | |
| 491 | + | |
| 492 | +Kernel response contents: | |
| 493 | + | |
| 494 | + ==================================== ====== ========================== | |
| 495 | + ``ETHTOOL_A_WOL_HEADER`` nested reply header | |
| 496 | + ``ETHTOOL_A_WOL_MODES`` bitset mask of enabled WoL modes | |
| 497 | + ``ETHTOOL_A_WOL_SOPASS`` binary SecureOn(tm) password | |
| 498 | + ==================================== ====== ========================== | |
| 499 | + | |
| 500 | +In reply, ``ETHTOOL_A_WOL_MODES`` mask consists of modes supported by the | |
| 501 | +device, value of modes which are enabled. ``ETHTOOL_A_WOL_SOPASS`` is only | |
| 502 | +included in reply if ``WAKE_MAGICSECURE`` mode is supported. | |
| 503 | + | |
| 504 | + | |
| 477 | 505 | Request translation |
| 478 | 506 | =================== |
| 479 | 507 | |
| @@ -490,7 +518,7 @@ have their netlink replacement yet. · 10 unmodified lines | ||
| 490 | 518 | ``ETHTOOL_MSG_LINKMODES_SET`` |
| 491 | 519 | ``ETHTOOL_GDRVINFO`` n/a |
| 492 | 520 | ``ETHTOOL_GREGS`` n/a |
| 493 | − ``ETHTOOL_GWOL`` n/a | |
| 521 | + ``ETHTOOL_GWOL`` ``ETHTOOL_MSG_WOL_GET`` | |
| 494 | 522 | ``ETHTOOL_SWOL`` n/a |
| 495 | 523 | ``ETHTOOL_GMSGLVL`` ``ETHTOOL_MSG_DEBUG_GET`` |
| 496 | 524 | ``ETHTOOL_SMSGLVL`` ``ETHTOOL_MSG_DEBUG_SET`` |
| @@ -595,6 +595,7 @@ struct ethtool_pauseparam { · 594 unmodified lines | ||
| 595 | 595 | * @ETH_SS_PHY_TUNABLES: PHY tunable names |
| 596 | 596 | * @ETH_SS_LINK_MODES: link mode names |
| 597 | 597 | * @ETH_SS_MSG_CLASSES: debug message class names |
| 598 | + * @ETH_SS_WOL_MODES: wake-on-lan modes | |
| 598 | 599 | */ |
| 599 | 600 | enum ethtool_stringset { |
| 600 | 601 | ETH_SS_TEST = 0, |
| @@ -608,6 +609,7 @@ enum ethtool_stringset { · 7 unmodified lines | ||
| 608 | 609 | ETH_SS_PHY_TUNABLES, |
| 609 | 610 | ETH_SS_LINK_MODES, |
| 610 | 611 | ETH_SS_MSG_CLASSES, |
| 612 | + ETH_SS_WOL_MODES, | |
| 611 | 613 | |
| 612 | 614 | /* add new constants above here */ |
| 613 | 615 | ETH_SS_COUNT |
| @@ -1695,6 +1697,8 @@ static inline int ethtool_validate_duplex(__u8 duplex) · 1081 unmodified lines | ||
| 1695 | 1697 | #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ |
| 1696 | 1698 | #define WAKE_FILTER (1 << 7) |
| 1697 | 1699 | |
| 1700 | +#define WOL_MODE_COUNT 8 | |
| 1701 | + | |
| 1698 | 1702 | /* L2-L4 network traffic flow types */ |
| 1699 | 1703 | #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ |
| 1700 | 1704 | #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ |
| @@ -22,6 +22,7 @@ enum { · 21 unmodified lines | ||
| 22 | 22 | ETHTOOL_MSG_LINKSTATE_GET, |
| 23 | 23 | ETHTOOL_MSG_DEBUG_GET, |
| 24 | 24 | ETHTOOL_MSG_DEBUG_SET, |
| 25 | + ETHTOOL_MSG_WOL_GET, | |
| 25 | 26 | |
| 26 | 27 | /* add new constants above here */ |
| 27 | 28 | __ETHTOOL_MSG_USER_CNT, |
| @@ -39,6 +40,7 @@ enum { · 11 unmodified lines | ||
| 39 | 40 | ETHTOOL_MSG_LINKSTATE_GET_REPLY, |
| 40 | 41 | ETHTOOL_MSG_DEBUG_GET_REPLY, |
| 41 | 42 | ETHTOOL_MSG_DEBUG_NTF, |
| 43 | + ETHTOOL_MSG_WOL_GET_REPLY, | |
| 42 | 44 | |
| 43 | 45 | /* add new constants above here */ |
| 44 | 46 | __ETHTOOL_MSG_KERNEL_CNT, |
| @@ -211,6 +213,19 @@ enum { · 166 unmodified lines | ||
| 211 | 213 | ETHTOOL_A_DEBUG_MAX = __ETHTOOL_A_DEBUG_CNT - 1 |
| 212 | 214 | }; |
| 213 | 215 | |
| 216 | +/* WOL */ | |
| 217 | + | |
| 218 | +enum { | |
| 219 | + ETHTOOL_A_WOL_UNSPEC, | |
| 220 | + ETHTOOL_A_WOL_HEADER, /* nest - _A_HEADER_* */ | |
| 221 | + ETHTOOL_A_WOL_MODES, /* bitset */ | |
| 222 | + ETHTOOL_A_WOL_SOPASS, /* binary */ | |
| 223 | + | |
| 224 | + /* add new constants above here */ | |
| 225 | + __ETHTOOL_A_WOL_CNT, | |
| 226 | + ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1 | |
| 227 | +}; | |
| 228 | + | |
| 214 | 229 | /* generic netlink info */ |
| 215 | 230 | #define ETHTOOL_GENL_NAME "ethtool" |
| 216 | 231 | #define ETHTOOL_GENL_VERSION 1 |
| @@ -5,4 +5,4 @@ obj-y += ioctl.o common.o · 4 unmodified lines | ||
| 5 | 5 | obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o |
| 6 | 6 | |
| 7 | 7 | ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ |
| 8 | − linkstate.o debug.o | |
| 8 | + linkstate.o debug.o wol.o | |
| @@ -190,6 +190,18 @@ const char netif_msg_class_names[][ETH_GSTRING_LEN] = { · 189 unmodified lines | ||
| 190 | 190 | }; |
| 191 | 191 | static_assert(ARRAY_SIZE(netif_msg_class_names) == NETIF_MSG_CLASS_COUNT); |
| 192 | 192 | |
| 193 | +const char wol_mode_names[][ETH_GSTRING_LEN] = { | |
| 194 | + [const_ilog2(WAKE_PHY)] = "phy", | |
| 195 | + [const_ilog2(WAKE_UCAST)] = "ucast", | |
| 196 | + [const_ilog2(WAKE_MCAST)] = "mcast", | |
| 197 | + [const_ilog2(WAKE_BCAST)] = "bcast", | |
| 198 | + [const_ilog2(WAKE_ARP)] = "arp", | |
| 199 | + [const_ilog2(WAKE_MAGIC)] = "magic", | |
| 200 | + [const_ilog2(WAKE_MAGICSECURE)] = "magicsecure", | |
| 201 | + [const_ilog2(WAKE_FILTER)] = "filter", | |
| 202 | +}; | |
| 203 | +static_assert(ARRAY_SIZE(wol_mode_names) == WOL_MODE_COUNT); | |
| 204 | + | |
| 193 | 205 | /* return false if legacy contained non-0 deprecated fields |
| 194 | 206 | * maxtxpkt/maxrxpkt. rest of ksettings always updated |
| 195 | 207 | */ |
| @@ -20,6 +20,7 @@ extern const char · 19 unmodified lines | ||
| 20 | 20 | phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; |
| 21 | 21 | extern const char link_mode_names[][ETH_GSTRING_LEN]; |
| 22 | 22 | extern const char netif_msg_class_names[][ETH_GSTRING_LEN]; |
| 23 | +extern const char wol_mode_names[][ETH_GSTRING_LEN]; | |
| 23 | 24 | |
| 24 | 25 | int __ethtool_get_link(struct net_device *dev); |
| 25 | 26 | |
| @@ -214,6 +214,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { · 213 unmodified lines | ||
| 214 | 214 | [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, |
| 215 | 215 | [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, |
| 216 | 216 | [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, |
| 217 | + [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, | |
| 217 | 218 | }; |
| 218 | 219 | |
| 219 | 220 | static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) |
| @@ -679,6 +680,14 @@ static const struct genl_ops ethtool_genl_ops[] = { · 459 unmodified lines | ||
| 679 | 680 | .flags = GENL_UNS_ADMIN_PERM, |
| 680 | 681 | .doit = ethnl_set_debug, |
| 681 | 682 | }, |
| 683 | + { | |
| 684 | + .cmd = ETHTOOL_MSG_WOL_GET, | |
| 685 | + .flags = GENL_UNS_ADMIN_PERM, | |
| 686 | + .doit = ethnl_default_doit, | |
| 687 | + .start = ethnl_default_start, | |
| 688 | + .dumpit = ethnl_default_dumpit, | |
| 689 | + .done = ethnl_default_done, | |
| 690 | + }, | |
| 682 | 691 | }; |
| 683 | 692 | |
| 684 | 693 | static const struct genl_multicast_group ethtool_nl_mcgrps[] = { |
| @@ -335,6 +335,7 @@ extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; · 334 unmodified lines | ||
| 335 | 335 | extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; |
| 336 | 336 | extern const struct ethnl_request_ops ethnl_linkstate_request_ops; |
| 337 | 337 | extern const struct ethnl_request_ops ethnl_debug_request_ops; |
| 338 | +extern const struct ethnl_request_ops ethnl_wol_request_ops; | |
| 338 | 339 | |
| 339 | 340 | int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); |
| 340 | 341 | int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); |
| @@ -55,6 +55,11 @@ static const struct strset_info info_template[] = { · 54 unmodified lines | ||
| 55 | 55 | .count = NETIF_MSG_CLASS_COUNT, |
| 56 | 56 | .strings = netif_msg_class_names, |
| 57 | 57 | }, |
| 58 | + [ETH_SS_WOL_MODES] = { | |
| 59 | + .per_dev = false, | |
| 60 | + .count = WOL_MODE_COUNT, | |
| 61 | + .strings = wol_mode_names, | |
| 62 | + }, | |
| 58 | 63 | }; |
| 59 | 64 | |
| 60 | 65 | struct strset_req_info { |
| @@ -0,0 +1,99 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | + | |
| 3 | +#include "netlink.h" | |
| 4 | +#include "common.h" | |
| 5 | +#include "bitset.h" | |
| 6 | + | |
| 7 | +struct wol_req_info { | |
| 8 | + struct ethnl_req_info base; | |
| 9 | +}; | |
| 10 | + | |
| 11 | +struct wol_reply_data { | |
| 12 | + struct ethnl_reply_data base; | |
| 13 | + struct ethtool_wolinfo wol; | |
| 14 | + bool show_sopass; | |
| 15 | +}; | |
| 16 | + | |
| 17 | +#define WOL_REPDATA(__reply_base) \ | |
| 18 | + container_of(__reply_base, struct wol_reply_data, base) | |
| 19 | + | |
| 20 | +static const struct nla_policy | |
| 21 | +wol_get_policy[ETHTOOL_A_WOL_MAX + 1] = { | |
| 22 | + [ETHTOOL_A_WOL_UNSPEC] = { .type = NLA_REJECT }, | |
| 23 | + [ETHTOOL_A_WOL_HEADER] = { .type = NLA_NESTED }, | |
| 24 | + [ETHTOOL_A_WOL_MODES] = { .type = NLA_REJECT }, | |
| 25 | + [ETHTOOL_A_WOL_SOPASS] = { .type = NLA_REJECT }, | |
| 26 | +}; | |
| 27 | + | |
| 28 | +static int wol_prepare_data(const struct ethnl_req_info *req_base, | |
| 29 | + struct ethnl_reply_data *reply_base, | |
| 30 | + struct genl_info *info) | |
| 31 | +{ | |
| 32 | + struct wol_reply_data *data = WOL_REPDATA(reply_base); | |
| 33 | + struct net_device *dev = reply_base->dev; | |
| 34 | + int ret; | |
| 35 | + | |
| 36 | + if (!dev->ethtool_ops->get_wol) | |
| 37 | + return -EOPNOTSUPP; | |
| 38 | + | |
| 39 | + ret = ethnl_ops_begin(dev); | |
| 40 | + if (ret < 0) | |
| 41 | + return ret; | |
| 42 | + dev->ethtool_ops->get_wol(dev, &data->wol); | |
| 43 | + ethnl_ops_complete(dev); | |
| 44 | + data->show_sopass = data->wol.supported & WAKE_MAGICSECURE; | |
| 45 | + | |
| 46 | + return 0; | |
| 47 | +} | |
| 48 | + | |
| 49 | +static int wol_reply_size(const struct ethnl_req_info *req_base, | |
| 50 | + const struct ethnl_reply_data *reply_base) | |
| 51 | +{ | |
| 52 | + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; | |
| 53 | + const struct wol_reply_data *data = WOL_REPDATA(reply_base); | |
| 54 | + int len; | |
| 55 | + | |
| 56 | + len = ethnl_bitset32_size(&data->wol.wolopts, &data->wol.supported, | |
| 57 | + WOL_MODE_COUNT, wol_mode_names, compact); | |
| 58 | + if (len < 0) | |
| 59 | + return len; | |
| 60 | + if (data->show_sopass) | |
| 61 | + len += nla_total_size(sizeof(data->wol.sopass)); | |
| 62 | + | |
| 63 | + return len; | |
| 64 | +} | |
| 65 | + | |
| 66 | +static int wol_fill_reply(struct sk_buff *skb, | |
| 67 | + const struct ethnl_req_info *req_base, | |
| 68 | + const struct ethnl_reply_data *reply_base) | |
| 69 | +{ | |
| 70 | + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; | |
| 71 | + const struct wol_reply_data *data = WOL_REPDATA(reply_base); | |
| 72 | + int ret; | |
| 73 | + | |
| 74 | + ret = ethnl_put_bitset32(skb, ETHTOOL_A_WOL_MODES, &data->wol.wolopts, | |
| 75 | + &data->wol.supported, WOL_MODE_COUNT, | |
| 76 | + wol_mode_names, compact); | |
| 77 | + if (ret < 0) | |
| 78 | + return ret; | |
| 79 | + if (data->show_sopass && | |
| 80 | + nla_put(skb, ETHTOOL_A_WOL_SOPASS, sizeof(data->wol.sopass), | |
| 81 | + data->wol.sopass)) | |
| 82 | + return -EMSGSIZE; | |
| 83 | + | |
| 84 | + return 0; | |
| 85 | +} | |
| 86 | + | |
| 87 | +const struct ethnl_request_ops ethnl_wol_request_ops = { | |
| 88 | + .request_cmd = ETHTOOL_MSG_WOL_GET, | |
| 89 | + .reply_cmd = ETHTOOL_MSG_WOL_GET_REPLY, | |
| 90 | + .hdr_attr = ETHTOOL_A_WOL_HEADER, | |
| 91 | + .max_attr = ETHTOOL_A_WOL_MAX, | |
| 92 | + .req_info_size = sizeof(struct wol_req_info), | |
| 93 | + .reply_data_size = sizeof(struct wol_reply_data), | |
| 94 | + .request_policy = wol_get_policy, | |
| 95 | + | |
| 96 | + .prepare_data = wol_prepare_data, | |
| 97 | + .reply_size = wol_reply_size, | |
| 98 | + .fill_reply = wol_fill_reply, | |
| 99 | +}; | |