Skip to content

Commit 041b1c5

Browse files
mkubecekdavem330
authored andcommitted
ethtool: helper functions for netlink interface
Add common request/reply header definition and helpers to parse request header and fill reply header. Provide ethnl_update_* helpers to update structure members from request attributes (to be used for *_SET requests). Signed-off-by: Michal Kubecek <[email protected]> Reviewed-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2b4a899 commit 041b1c5

File tree

3 files changed

+392
-0
lines changed

3 files changed

+392
-0
lines changed

include/uapi/linux/ethtool_netlink.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@ enum {
2929
ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1
3030
};
3131

32+
/* request header */
33+
34+
/* use compact bitsets in reply */
35+
#define ETHTOOL_FLAG_COMPACT_BITSETS (1 << 0)
36+
/* provide optional reply for SET or ACT requests */
37+
#define ETHTOOL_FLAG_OMIT_REPLY (1 << 1)
38+
39+
#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
40+
ETHTOOL_FLAG_OMIT_REPLY)
41+
42+
enum {
43+
ETHTOOL_A_HEADER_UNSPEC,
44+
ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */
45+
ETHTOOL_A_HEADER_DEV_NAME, /* string */
46+
ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */
47+
48+
/* add new constants above here */
49+
__ETHTOOL_A_HEADER_CNT,
50+
ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1
51+
};
52+
3253
/* generic netlink info */
3354
#define ETHTOOL_GENL_NAME "ethtool"
3455
#define ETHTOOL_GENL_VERSION 1

net/ethtool/netlink.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,174 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22

3+
#include <net/sock.h>
34
#include <linux/ethtool_netlink.h>
45
#include "netlink.h"
56

7+
static struct genl_family ethtool_genl_family;
8+
9+
static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = {
10+
[ETHTOOL_A_HEADER_UNSPEC] = { .type = NLA_REJECT },
11+
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
12+
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
13+
.len = ALTIFNAMSIZ - 1 },
14+
[ETHTOOL_A_HEADER_FLAGS] = { .type = NLA_U32 },
15+
};
16+
17+
/**
18+
* ethnl_parse_header() - parse request header
19+
* @req_info: structure to put results into
20+
* @header: nest attribute with request header
21+
* @net: request netns
22+
* @extack: netlink extack for error reporting
23+
* @require_dev: fail if no device identified in header
24+
*
25+
* Parse request header in nested attribute @nest and puts results into
26+
* the structure pointed to by @req_info. Extack from @info is used for error
27+
* reporting. If req_info->dev is not null on return, reference to it has
28+
* been taken. If error is returned, *req_info is null initialized and no
29+
* reference is held.
30+
*
31+
* Return: 0 on success or negative error code
32+
*/
33+
int ethnl_parse_header(struct ethnl_req_info *req_info,
34+
const struct nlattr *header, struct net *net,
35+
struct netlink_ext_ack *extack, bool require_dev)
36+
{
37+
struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1];
38+
const struct nlattr *devname_attr;
39+
struct net_device *dev = NULL;
40+
int ret;
41+
42+
if (!header) {
43+
NL_SET_ERR_MSG(extack, "request header missing");
44+
return -EINVAL;
45+
}
46+
ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
47+
ethnl_header_policy, extack);
48+
if (ret < 0)
49+
return ret;
50+
devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
51+
52+
if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
53+
u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]);
54+
55+
dev = dev_get_by_index(net, ifindex);
56+
if (!dev) {
57+
NL_SET_ERR_MSG_ATTR(extack,
58+
tb[ETHTOOL_A_HEADER_DEV_INDEX],
59+
"no device matches ifindex");
60+
return -ENODEV;
61+
}
62+
/* if both ifindex and ifname are passed, they must match */
63+
if (devname_attr &&
64+
strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) {
65+
dev_put(dev);
66+
NL_SET_ERR_MSG_ATTR(extack, header,
67+
"ifindex and name do not match");
68+
return -ENODEV;
69+
}
70+
} else if (devname_attr) {
71+
dev = dev_get_by_name(net, nla_data(devname_attr));
72+
if (!dev) {
73+
NL_SET_ERR_MSG_ATTR(extack, devname_attr,
74+
"no device matches name");
75+
return -ENODEV;
76+
}
77+
} else if (require_dev) {
78+
NL_SET_ERR_MSG_ATTR(extack, header,
79+
"neither ifindex nor name specified");
80+
return -EINVAL;
81+
}
82+
83+
if (dev && !netif_device_present(dev)) {
84+
dev_put(dev);
85+
NL_SET_ERR_MSG(extack, "device not present");
86+
return -ENODEV;
87+
}
88+
89+
req_info->dev = dev;
90+
if (tb[ETHTOOL_A_HEADER_FLAGS])
91+
req_info->flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
92+
93+
return 0;
94+
}
95+
96+
/**
97+
* ethnl_fill_reply_header() - Put common header into a reply message
98+
* @skb: skb with the message
99+
* @dev: network device to describe in header
100+
* @attrtype: attribute type to use for the nest
101+
*
102+
* Create a nested attribute with attributes describing given network device.
103+
*
104+
* Return: 0 on success, error value (-EMSGSIZE only) on error
105+
*/
106+
int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev,
107+
u16 attrtype)
108+
{
109+
struct nlattr *nest;
110+
111+
if (!dev)
112+
return 0;
113+
nest = nla_nest_start(skb, attrtype);
114+
if (!nest)
115+
return -EMSGSIZE;
116+
117+
if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) ||
118+
nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name))
119+
goto nla_put_failure;
120+
/* If more attributes are put into reply header, ethnl_header_size()
121+
* must be updated to account for them.
122+
*/
123+
124+
nla_nest_end(skb, nest);
125+
return 0;
126+
127+
nla_put_failure:
128+
nla_nest_cancel(skb, nest);
129+
return -EMSGSIZE;
130+
}
131+
132+
/**
133+
* ethnl_reply_init() - Create skb for a reply and fill device identification
134+
* @payload: payload length (without netlink and genetlink header)
135+
* @dev: device the reply is about (may be null)
136+
* @cmd: ETHTOOL_MSG_* message type for reply
137+
* @info: genetlink info of the received packet we respond to
138+
* @ehdrp: place to store payload pointer returned by genlmsg_new()
139+
*
140+
* Return: pointer to allocated skb on success, NULL on error
141+
*/
142+
struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
143+
u16 hdr_attrtype, struct genl_info *info,
144+
void **ehdrp)
145+
{
146+
struct sk_buff *skb;
147+
148+
skb = genlmsg_new(payload, GFP_KERNEL);
149+
if (!skb)
150+
goto err;
151+
*ehdrp = genlmsg_put_reply(skb, info, &ethtool_genl_family, 0, cmd);
152+
if (!*ehdrp)
153+
goto err_free;
154+
155+
if (dev) {
156+
int ret;
157+
158+
ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype);
159+
if (ret < 0)
160+
goto err_free;
161+
}
162+
return skb;
163+
164+
err_free:
165+
nlmsg_free(skb);
166+
err:
167+
if (info)
168+
GENL_SET_ERR_MSG(info, "failed to setup reply message");
169+
return NULL;
170+
}
171+
6172
/* genetlink setup */
7173

8174
static const struct genl_ops ethtool_genl_ops[] = {

0 commit comments

Comments
 (0)