Misc helpers used by ethtool netlink code.

Signed-off-by: Michal Kubecek <mkube...@suse.cz>
---
 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, &ethtool_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

Reply via email to