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 | ``ETHTOOL_MSG_LINKSTATE_GET`` get link state | 187 | ``ETHTOOL_MSG_LINKSTATE_GET`` get link state |
| 188 | ``ETHTOOL_MSG_DEBUG_GET`` get debugging settings | 188 | ``ETHTOOL_MSG_DEBUG_GET`` get debugging settings |
| 189 | ``ETHTOOL_MSG_DEBUG_SET`` set debugging settings | 189 | ``ETHTOOL_MSG_DEBUG_SET`` set debugging settings |
| 190 | ``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings | ||
| 190 | ===================================== ================================ | 191 | ===================================== ================================ |
| 191 | 192 | ||
| 192 | Kernel to userspace: | 193 | Kernel to userspace: |
| @@ -200,6 +201,7 @@ Kernel to userspace: · 7 unmodified lines | |||
| 200 | ``ETHTOOL_MSG_LINKSTATE_GET_REPLY`` link state info | 201 | ``ETHTOOL_MSG_LINKSTATE_GET_REPLY`` link state info |
| 201 | ``ETHTOOL_MSG_DEBUG_GET_REPLY`` debugging settings | 202 | ``ETHTOOL_MSG_DEBUG_GET_REPLY`` debugging settings |
| 202 | ``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification | 203 | ``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification |
| 204 | ``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings | ||
| 203 | ===================================== ================================ | 205 | ===================================== ================================ |
| 204 | 206 | ||
| 205 | ``GET`` requests are sent by userspace applications to retrieve device | 207 | ``GET`` requests are sent by userspace applications to retrieve device |
| @@ -474,6 +476,32 @@ Request contents: · 268 unmodified lines | |||
| 474 | enabled debugging message types for the device. | 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 | Request translation | 505 | Request translation |
| 478 | =================== | 506 | =================== |
| 479 | 507 | ||
| @@ -490,7 +518,7 @@ have their netlink replacement yet. · 10 unmodified lines | |||
| 490 | ``ETHTOOL_MSG_LINKMODES_SET`` | 518 | ``ETHTOOL_MSG_LINKMODES_SET`` |
| 491 | ``ETHTOOL_GDRVINFO`` n/a | 519 | ``ETHTOOL_GDRVINFO`` n/a |
| 492 | ``ETHTOOL_GREGS`` n/a | 520 | ``ETHTOOL_GREGS`` n/a |
| 493 | ``ETHTOOL_GWOL`` n/a | 521 | ``ETHTOOL_GWOL`` ``ETHTOOL_MSG_WOL_GET`` |
| 494 | ``ETHTOOL_SWOL`` n/a | 522 | ``ETHTOOL_SWOL`` n/a |
| 495 | ``ETHTOOL_GMSGLVL`` ``ETHTOOL_MSG_DEBUG_GET`` | 523 | ``ETHTOOL_GMSGLVL`` ``ETHTOOL_MSG_DEBUG_GET`` |
| 496 | ``ETHTOOL_SMSGLVL`` ``ETHTOOL_MSG_DEBUG_SET`` | 524 | ``ETHTOOL_SMSGLVL`` ``ETHTOOL_MSG_DEBUG_SET`` |
| @@ -595,6 +595,7 @@ struct ethtool_pauseparam { · 594 unmodified lines | |||
| 595 | * @ETH_SS_PHY_TUNABLES: PHY tunable names | 595 | * @ETH_SS_PHY_TUNABLES: PHY tunable names |
| 596 | * @ETH_SS_LINK_MODES: link mode names | 596 | * @ETH_SS_LINK_MODES: link mode names |
| 597 | * @ETH_SS_MSG_CLASSES: debug message class names | 597 | * @ETH_SS_MSG_CLASSES: debug message class names |
| 598 | * @ETH_SS_WOL_MODES: wake-on-lan modes | ||
| 598 | */ | 599 | */ |
| 599 | enum ethtool_stringset { | 600 | enum ethtool_stringset { |
| 600 | ETH_SS_TEST = 0, | 601 | ETH_SS_TEST = 0, |
| @@ -608,6 +609,7 @@ enum ethtool_stringset { · 7 unmodified lines | |||
| 608 | ETH_SS_PHY_TUNABLES, | 609 | ETH_SS_PHY_TUNABLES, |
| 609 | ETH_SS_LINK_MODES, | 610 | ETH_SS_LINK_MODES, |
| 610 | ETH_SS_MSG_CLASSES, | 611 | ETH_SS_MSG_CLASSES, |
| 612 | ETH_SS_WOL_MODES, | ||
| 611 | 613 | ||
| 612 | /* add new constants above here */ | 614 | /* add new constants above here */ |
| 613 | ETH_SS_COUNT | 615 | ETH_SS_COUNT |
| @@ -1695,6 +1697,8 @@ static inline int ethtool_validate_duplex(__u8 duplex) · 1081 unmodified lines | |||
| 1695 | #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ | 1697 | #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ |
| 1696 | #define WAKE_FILTER (1 << 7) | 1698 | #define WAKE_FILTER (1 << 7) |
| 1697 | 1699 | ||
| 1700 | #define WOL_MODE_COUNT 8 | ||
| 1701 | |||
| 1698 | /* L2-L4 network traffic flow types */ | 1702 | /* L2-L4 network traffic flow types */ |
| 1699 | #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ | 1703 | #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ |
| 1700 | #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ | 1704 | #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ |
| @@ -22,6 +22,7 @@ enum { · 21 unmodified lines | |||
| 22 | ETHTOOL_MSG_LINKSTATE_GET, | 22 | ETHTOOL_MSG_LINKSTATE_GET, |
| 23 | ETHTOOL_MSG_DEBUG_GET, | 23 | ETHTOOL_MSG_DEBUG_GET, |
| 24 | ETHTOOL_MSG_DEBUG_SET, | 24 | ETHTOOL_MSG_DEBUG_SET, |
| 25 | ETHTOOL_MSG_WOL_GET, | ||
| 25 | 26 | ||
| 26 | /* add new constants above here */ | 27 | /* add new constants above here */ |
| 27 | __ETHTOOL_MSG_USER_CNT, | 28 | __ETHTOOL_MSG_USER_CNT, |
| @@ -39,6 +40,7 @@ enum { · 11 unmodified lines | |||
| 39 | ETHTOOL_MSG_LINKSTATE_GET_REPLY, | 40 | ETHTOOL_MSG_LINKSTATE_GET_REPLY, |
| 40 | ETHTOOL_MSG_DEBUG_GET_REPLY, | 41 | ETHTOOL_MSG_DEBUG_GET_REPLY, |
| 41 | ETHTOOL_MSG_DEBUG_NTF, | 42 | ETHTOOL_MSG_DEBUG_NTF, |
| 43 | ETHTOOL_MSG_WOL_GET_REPLY, | ||
| 42 | 44 | ||
| 43 | /* add new constants above here */ | 45 | /* add new constants above here */ |
| 44 | __ETHTOOL_MSG_KERNEL_CNT, | 46 | __ETHTOOL_MSG_KERNEL_CNT, |
| @@ -211,6 +213,19 @@ enum { · 166 unmodified lines | |||
| 211 | ETHTOOL_A_DEBUG_MAX = __ETHTOOL_A_DEBUG_CNT - 1 | 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 | /* generic netlink info */ | 229 | /* generic netlink info */ |
| 215 | #define ETHTOOL_GENL_NAME "ethtool" | 230 | #define ETHTOOL_GENL_NAME "ethtool" |
| 216 | #define ETHTOOL_GENL_VERSION 1 | 231 | #define ETHTOOL_GENL_VERSION 1 |
| @@ -5,4 +5,4 @@ obj-y += ioctl.o common.o · 4 unmodified lines | |||
| 5 | obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o | 5 | obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o |
| 6 | 6 | ||
| 7 | ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ | 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 | static_assert(ARRAY_SIZE(netif_msg_class_names) == NETIF_MSG_CLASS_COUNT); | 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 | /* return false if legacy contained non-0 deprecated fields | 205 | /* return false if legacy contained non-0 deprecated fields |
| 194 | * maxtxpkt/maxrxpkt. rest of ksettings always updated | 206 | * maxtxpkt/maxrxpkt. rest of ksettings always updated |
| 195 | */ | 207 | */ |
| @@ -20,6 +20,7 @@ extern const char · 19 unmodified lines | |||
| 20 | phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; | 20 | phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; |
| 21 | extern const char link_mode_names[][ETH_GSTRING_LEN]; | 21 | extern const char link_mode_names[][ETH_GSTRING_LEN]; |
| 22 | extern const char netif_msg_class_names[][ETH_GSTRING_LEN]; | 22 | extern const char netif_msg_class_names[][ETH_GSTRING_LEN]; |
| 23 | extern const char wol_mode_names[][ETH_GSTRING_LEN]; | ||
| 23 | 24 | ||
| 24 | int __ethtool_get_link(struct net_device *dev); | 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 | [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, | 214 | [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, |
| 215 | [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, | 215 | [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, |
| 216 | [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, | 216 | [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, |
| 217 | [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, | ||
| 217 | }; | 218 | }; |
| 218 | 219 | ||
| 219 | static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) | 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 | .flags = GENL_UNS_ADMIN_PERM, | 680 | .flags = GENL_UNS_ADMIN_PERM, |
| 680 | .doit = ethnl_set_debug, | 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 | static const struct genl_multicast_group ethtool_nl_mcgrps[] = { | 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 | extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; | 335 | extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; |
| 336 | extern const struct ethnl_request_ops ethnl_linkstate_request_ops; | 336 | extern const struct ethnl_request_ops ethnl_linkstate_request_ops; |
| 337 | extern const struct ethnl_request_ops ethnl_debug_request_ops; | 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 | int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); | 340 | int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); |
| 340 | int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); | 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 | .count = NETIF_MSG_CLASS_COUNT, | 55 | .count = NETIF_MSG_CLASS_COUNT, |
| 56 | .strings = netif_msg_class_names, | 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 | struct strset_req_info { | 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 | }; | ||