From patchwork Mon Feb 18 18:21:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 1044210 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.cz Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 443Bz134JZz9s9G for ; Tue, 19 Feb 2019 05:21:49 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2403864AbfBRSVs (ORCPT ); Mon, 18 Feb 2019 13:21:48 -0500 Received: from mx2.suse.de ([195.135.220.15]:46096 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2391469AbfBRSVq (ORCPT ); Mon, 18 Feb 2019 13:21:46 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 00632AEB8; Mon, 18 Feb 2019 18:21:45 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id 9CACFE0122; Mon, 18 Feb 2019 19:21:44 +0100 (CET) Message-Id: <266b4ea3596de134329f8bbd5d13e282d1d27442.1550513384.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [RFC PATCH net-next v3 04/21] ethtool: helper functions for netlink interface To: netdev@vger.kernel.org Cc: David Miller , Andrew Lunn , Jakub Kicinski , Jiri Pirko , linux-kernel@vger.kernel.org Date: Mon, 18 Feb 2019 19:21:44 +0100 (CET) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Various helpers used by ethtool netlink code. Signed-off-by: Michal Kubecek --- include/uapi/linux/ethtool_netlink.h | 11 +++ net/ethtool/netlink.c | 119 +++++++++++++++++++++++ net/ethtool/netlink.h | 135 +++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index d8a8d3c72c2f..01896a1ee21a 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -12,6 +12,17 @@ enum { ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1) }; +/* device specification */ + +enum { + ETHA_DEV_UNSPEC, + ETHA_DEV_INDEX, /* u32 */ + ETHA_DEV_NAME, /* string */ + + __ETHA_DEV_CNT, + ETHA_DEV_MAX = (__ETHA_DEV_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 5fe3e5b68e81..ffdcc7c9d4bc 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -1,8 +1,127 @@ // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +#include #include #include "netlink.h" +static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = { + [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT }, + [ETHA_DEV_INDEX] = { .type = NLA_U32 }, + [ETHA_DEV_NAME] = { .type = NLA_NUL_STRING, + .len = IFNAMSIZ - 1 }, +}; + +struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest) +{ + struct net *net = genl_info_net(info); + struct nlattr *tb[ETHA_DEV_MAX + 1]; + struct net_device *dev; + int ret; + + if (!nest) { + ETHNL_SET_ERRMSG(info, + "mandatory device identification missing"); + return ERR_PTR(-EINVAL); + } + ret = nla_parse_nested(tb, ETHA_DEV_MAX, nest, dev_policy, + info->extack); + if (ret < 0) + return ERR_PTR(ret); + + if (tb[ETHA_DEV_INDEX]) { + dev = dev_get_by_index(net, nla_get_u32(tb[ETHA_DEV_INDEX])); + if (!dev) + return ERR_PTR(-ENODEV); + /* if both ifindex and ifname are passed, they must match */ + if (tb[ETHA_DEV_NAME]) { + const char *nl_name = nla_data(tb[ETHA_DEV_NAME]); + + if (strncmp(dev->name, nl_name, IFNAMSIZ)) { + dev_put(dev); + ETHNL_SET_ERRMSG(info, + "ifindex and ifname do not match"); + return ERR_PTR(-ENODEV); + } + } + return dev; + } else if (tb[ETHA_DEV_NAME]) { + dev = dev_get_by_name(net, nla_data(tb[ETHA_DEV_NAME])); + if (!dev) + return ERR_PTR(-ENODEV); + } else { + ETHNL_SET_ERRMSG(info, "either ifindex or ifname required"); + return ERR_PTR(-EINVAL); + } + + if (!netif_device_present(dev)) { + dev_put(dev); + ETHNL_SET_ERRMSG(info, "device not present"); + return ERR_PTR(-ENODEV); + } + return dev; +} + +int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype) +{ + struct nlattr *nest; + int ret = -EMSGSIZE; + + nest = ethnl_nest_start(msg, attrtype); + if (!nest) + return -EMSGSIZE; + + if (nla_put_u32(msg, ETHA_DEV_INDEX, (u32)dev->ifindex)) + goto err; + if (nla_put_string(msg, ETHA_DEV_NAME, dev->name)) + goto err; + + nla_nest_end(msg, nest); + return 0; +err: + nla_nest_cancel(msg, nest); + return ret; +} + +/* create skb for a reply and fill device identification + * payload: payload length (without netlink and genetlink header) + * dev: device the reply is about (may be null) + * cmd: ETHNL_CMD_* command for reply + * info: info for the received packet we respond to + * ehdrp: place to store payload pointer returned by genlmsg_new() + * returns: skb or null on error + */ +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd, + u16 dev_attrtype, struct genl_info *info, + void **ehdrp) +{ + void *ehdr; + struct sk_buff *rskb; + + rskb = genlmsg_new(payload, GFP_KERNEL); + if (!rskb) { + ETHNL_SET_ERRMSG(info, + "failed to allocate reply message"); + return NULL; + } + + ehdr = genlmsg_put_reply(rskb, info, ðtool_genl_family, 0, cmd); + if (!ehdr) + goto err; + if (ehdrp) + *ehdrp = ehdr; + if (dev) { + int ret = ethnl_fill_dev(rskb, dev, dev_attrtype); + + if (ret < 0) + goto err; + } + + return rskb; +err: + nlmsg_free(rskb); + return NULL; +} + /* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 63063b582ca2..36a397656127 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -6,7 +6,142 @@ #include #include #include +#include + +#define ETHNL_SET_ERRMSG(info, msg) \ + do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0) extern struct genl_family ethtool_genl_family; +struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest); +int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype); + +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd, + u16 dev_attrtype, struct genl_info *info, + void **ehdrp); + +static inline int ethnl_str_size(const char *s) +{ + return nla_total_size(strlen(s) + 1); +} + +static inline int ethnl_str_ifne_size(const char *s) +{ + return s[0] ? ethnl_str_size(s) : 0; +} + +static inline 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 inline 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 inline 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 inline 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 inline 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 inline 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 inline 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 inline void warn_partial_info(struct genl_info *info) +{ + ETHNL_SET_ERRMSG(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 inline bool ethnl_is_privileged(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + + return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN); +} + +/* total size of ETHA_*_DEV nested attribute; this is an upper estimate so that + * we do not need to hold RTNL longer than necessary to prevent rename between + * estimating the size and composing the message + */ +static inline unsigned int dev_ident_size(void) +{ + return nla_total_size(nla_total_size(sizeof(u32)) + + nla_total_size(IFNAMSIZ)); +} + #endif /* _NET_ETHTOOL_NETLINK_H */