[RFC PATCH 3/9] ethtool: helper functions for netlink interface
From: Michal Kubecek
Date: Mon Dec 11 2017 - 08:53:56 EST
Misc helpers used by ethtool netlink code.
Signed-off-by: Michal Kubecek <mkubecek@xxxxxxx>
---
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