Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753190AbdLKNxx (ORCPT ); Mon, 11 Dec 2017 08:53:53 -0500 Received: from mx2.suse.de ([195.135.220.15]:60148 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752629AbdLKNxo (ORCPT ); Mon, 11 Dec 2017 08:53:44 -0500 Message-Id: <0fc799575dd3893a386cfa9be35b17bf9ced4094.1513000306.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [RFC PATCH 3/9] ethtool: helper functions for netlink interface To: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Date: Mon, 11 Dec 2017 14:53:41 +0100 (CET) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4689 Lines: 197 Misc helpers used by ethtool netlink code. Signed-off-by: Michal Kubecek --- net/core/ethtool_netlink.c | 177 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c index 46a226bb9a2c..22d23d057623 100644 --- a/net/core/ethtool_netlink.c +++ b/net/core/ethtool_netlink.c @@ -8,6 +8,183 @@ static struct genl_family ethtool_genl_family; +/* misc helper functions */ + +static int ethnl_str_size(const char *s) +{ + return nla_total_size(strlen(s) + 1); +} + +static int ethnl_str_ifne_size(const char *s) +{ + return s[0] ? ethnl_str_size(s) : 0; +} + +static int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype, const char *s) +{ + if (!s[0]) + return 0; + return nla_put_string(skb, attrtype, s); +} + +static struct nlattr *ethnl_nest_start(struct sk_buff *skb, int attrtype) +{ + return nla_nest_start(skb, attrtype | NLA_F_NESTED); +} + +/* ethnl_update_* return true if the value is changed */ +static bool ethnl_update_u32(u32 *dst, struct nlattr *attr) +{ + u32 val; + + if (!attr) + return false; + val = nla_get_u32(attr); + if (*dst == val) + return false; + + *dst = val; + return true; +} + +static bool ethnl_update_u8(u8 *dst, struct nlattr *attr) +{ + u8 val; + + if (!attr) + return false; + val = nla_get_u8(attr); + if (*dst == val) + return false; + + *dst = val; + return true; +} + +/* update u32 value used as bool from NLA_U8 */ +static bool ethnl_update_bool32(u32 *dst, struct nlattr *attr) +{ + u8 val; + + if (!attr) + return false; + val = !!nla_get_u8(attr); + if (!!*dst == val) + return false; + + *dst = val; + return true; +} + +static bool ethnl_update_binary(u8 *dst, unsigned int len, struct nlattr *attr) +{ + if (!attr) + return false; + if (nla_len(attr) < len) + len = nla_len(attr); + if (!memcmp(dst, nla_data(attr), len)) + return false; + + memcpy(dst, nla_data(attr), len); + return true; +} + +static bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr) +{ + struct nla_bitfield32 change; + u32 newval; + + if (!attr) + return false; + change = nla_get_bitfield32(attr); + newval = (*dst & ~change.selector) | (change.value & change.selector); + if (*dst == newval) + return false; + + *dst = newval; + return true; +} + +static struct net_device *ethnl_dev_get(struct genl_info *info) +{ + const struct ethnlmsghdr *hdr = info->userhdr; + struct net *net = genl_info_net(info); + struct net_device *dev; + + if (hdr->ifindex) { + dev = dev_get_by_index(net, hdr->ifindex); + if (!dev) + return ERR_PTR(-ENODEV); + /* if both ifindex and ifname are passed, both must match */ + if (hdr->ifname && strncmp(dev->name, hdr->ifname, IFNAMSIZ)) { + GENL_SET_ERR_MSG(info, + "ifindex and ifname do not match"); + return ERR_PTR(-ENODEV); + } + return dev; + } else if (hdr->ifname[0]) { + dev = dev_get_by_name(net, hdr->ifname); + return dev ?: ERR_PTR(-ENODEV); + } + + return ERR_PTR(-EINVAL); +} + +static void warn_partial_info(struct genl_info *info) +{ + GENL_SET_ERR_MSG(info, "not all requested data could be retrieved"); +} + +/* Check user privileges explicitly to allow finer access control based on + * context of the request or hiding part of the information from unprivileged + * users + */ +static bool ethnl_is_privileged(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + + return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN); +} + +/* create skb for a reply + * payload: payload length (without netlink, genetlink and ethnl headers) + * dev: device the reply is about + * cmd: ETHTOOL_CMD_* command for reply + * info: info for the received packet we respond to + * ehdrp: place to store pointer to ethtool specific header (may be null) + * returns: skb or null on error + */ +static struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, + u8 cmd, struct genl_info *info, + struct ethnlmsghdr **ehdrp) +{ + struct ethnlmsghdr *ehdr; + struct sk_buff *rskb; + + rskb = genlmsg_new(ETHNL_HDRLEN + payload, GFP_KERNEL); + if (!rskb) { + GENL_SET_ERR_MSG(info, + "failed to allocate reply message"); + return NULL; + } + + ehdr = genlmsg_put_reply(rskb, info, ðtool_genl_family, 0, cmd); + if (!ehdr) { + nlmsg_free(rskb); + return NULL; + } + if (ehdrp) + *ehdrp = ehdr; + + ehdr->ifindex = dev->ifindex; + ehdr->flags = 0; + ehdr->info_mask = 0; + strncpy(ehdr->ifname, dev->name, sizeof(ehdr->ifname)); + ehdr->ifname[sizeof(ehdr->ifname) - 1] = '\0'; + + return rskb; +} + /* genetlink paperwork */ static const struct genl_ops ethtool_genl_ops[] = { -- 2.15.1