[RFC,v3,03/19] batman-adv: Prepare framework for hardif genl config

Message ID 20181207135846.6152-4-sven@narfation.org (mailing list archive)
State RFC, archived
Delegated to: Simon Wunderlich
Headers
Series batman-adv: netlink restructuring, part 2 |

Commit Message

Sven Eckelmann Dec. 7, 2018, 1:58 p.m. UTC
  The batman-adv configuration interface was implemented solely using sysfs.
This approach was condemned by non-batadv developers as "huge mistake".
Instead a netlink/genl based implementation was suggested.

Beside the mesh/soft-interface specific configuration, the
slave/hard-interface have B.A.T.M.A.N. V specific configuration settings.
The genl interface reflects this by allowing to get/set it using the
hard-interface specific commands.

The BATADV_CMD_GET_HARDIFS (or short version BATADV_CMD_GET_HARDIF) is
reused as get command because it already allow to dump the content of other
information from the slave/hard-interface which are not yet configuration
specific.

The set command BATADV_CMD_SET_HARDIF will also notify interested userspace
listeners of the "config" mcast group using the BATADV_CMD_SET_HARDIF
command message type that settings might have been changed and what the
current values are.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 include/uapi/linux/batman_adv.h |  16 ++-
 net/batman-adv/netlink.c        | 237 +++++++++++++++++++++++++++++---
 2 files changed, 230 insertions(+), 23 deletions(-)
  

Comments

Linus Lüssing Dec. 31, 2018, 11:59 a.m. UTC | #1
On Fri, Dec 07, 2018 at 02:58:30PM +0100, Sven Eckelmann wrote:
[...]
> The BATADV_CMD_GET_HARDIFS (or short version BATADV_CMD_GET_HARDIF) is
> reused as get command because it already allow to dump the content of other
> information from the slave/hard-interface which are not yet configuration
> specific.

"already allow" -> "already allows"

[...]
> @@ -76,6 +77,13 @@ enum batadv_genl_ops_flags {
>  	 *  safed in info->user_ptr[0]
>  	 */
>  	BATADV_FLAG_NEED_MESH = BIT(0),
> +
> +	/**
> +	 * @BATADV_FLAG_NEED_HARDIF: request requires valid hard interface in
> +	 *  attribute BATADV_ATTR_HARD_IFINDEX and expects a pointer to it to be
> +	 *  safed in info->user_ptr[1]
> +	 */
> +	BATADV_FLAG_NEED_HARDIF = BIT(1),

safed -> saved
(and same in the previous patch)

[...]
>  /**
>   * batadv_pre_doit() - Prepare batman-adv genl doit request
>   * @ops: requested netlink operation
> @@ -592,7 +738,16 @@ batadv_get_softif_from_info(struct net *net, struct genl_info *info)
>  static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
>  			   struct genl_info *info)
>  {
> -	struct batadv_priv *bat_priv;
> +	struct batadv_hard_iface *hard_iface;
> +	struct batadv_priv *bat_priv = NULL;
> +	int ret;
> +
> +	if (WARN_ON(ops->internal_flags & BATADV_FLAG_NEED_HARDIF))
> +		return -EINVAL;
> +
> +	if (WARN_ON((ops->internal_flags & BATADV_FLAG_NEED_HARDIF) &&
> +		    (~ops->internal_flags & BATADV_FLAG_NEED_MESH)))
> +		return -EINVAL;

Seems like the second part is unreachable code. If
BATADV_FLAG_NEED_HARDIF is set then the function returns with the first
if-clause already.
  
Sven Eckelmann Dec. 31, 2018, 7:17 p.m. UTC | #2
On Monday, 31 December 2018 12.59.36 CET Linus Lüssing wrote:
[...]
> >  static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
> >  			   struct genl_info *info)
> >  {
> > -	struct batadv_priv *bat_priv;
> > +	struct batadv_hard_iface *hard_iface;
> > +	struct batadv_priv *bat_priv = NULL;
> > +	int ret;
> > +
> > +	if (WARN_ON(ops->internal_flags & BATADV_FLAG_NEED_HARDIF))
> > +		return -EINVAL;
> > +
> > +	if (WARN_ON((ops->internal_flags & BATADV_FLAG_NEED_HARDIF) &&
> > +		    (~ops->internal_flags & BATADV_FLAG_NEED_MESH)))
> > +		return -EINVAL;
> 
> Seems like the second part is unreachable code. If
> BATADV_FLAG_NEED_HARDIF is set then the function returns with the first
> if-clause already.

Looks like I've incorrectly splitted the patch at this position. Makes more 
sense when you check the next patch.

So the first check should be removed from this patch to make it less 
confusing.


Btw. thanks for the spelling/grammar fixes.

Kind regards,
	Sven
  
Linus Lüssing Jan. 4, 2019, 12:39 a.m. UTC | #3
On Fri, Dec 07, 2018 at 02:58:30PM +0100, Sven Eckelmann wrote:
[...]
> +/**
> + * batadv_get_hardif_from_info() - Retrieve hardif from genl attributes
> + * @bat_priv: the bat priv with all the soft interface information
> + * @net: the applicable net namespace
> + * @info: receiver information
> + *
> + * Return: Pointer to hard interface on success, error pointer on error
> + */
> +static struct batadv_hard_iface *
> +batadv_get_hardif_from_info(struct batadv_priv *bat_priv, struct net *net,
> +			    struct genl_info *info)
> +{
> +	struct batadv_hard_iface *hard_iface;
> +	struct net_device *hard_dev;
> +	unsigned int hardif_index;
> +
> +	if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
> +		return ERR_PTR(-EINVAL);
> +
> +	hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
> +
> +	hard_dev = dev_get_by_index(net, hardif_index);
> +	if (!hard_dev)
> +		return ERR_PTR(-ENODEV);
> +
> +	hard_iface = batadv_hardif_get_by_netdev(hard_dev);
> +	if (!hard_iface)
> +		goto err_put_harddev;
> +
> +	if (hard_iface->soft_iface != bat_priv->soft_iface)
> +		goto err_put_hardif;

When would this case above happen?

> +
> +	return hard_iface;

It seems unnecessary to keep holding a reference to hard_dev on
successful return here (and releasing it in post_doit). We return
hard_iface and increase its reference count which in turn itself holds
a reference to the according hard_dev already.

The usual pattern for a getter like this would be to increase the
reference count for just the object returned, wouldn't it?

In any case, maybe it would make sense to mention increased
refcounts in the kerneldoc? So that in case someone were reusing
this function that s/he would not miss the decrease. (happens too
often with these net_device reference counters...) Same for
batadv_get_softif_from_info().

> +
> +err_put_hardif:
> +	batadv_hardif_put(hard_iface);
> +err_put_harddev:
> +	dev_put(hard_dev);
> +
> +	return ERR_PTR(-EINVAL);
> +}
> +
  
Sven Eckelmann Jan. 4, 2019, 7:52 a.m. UTC | #4
On Friday, 4 January 2019 01.39.54 CET Linus Lüssing wrote:
> On Fri, Dec 07, 2018 at 02:58:30PM +0100, Sven Eckelmann wrote:
> [...]
> > +/**
> > + * batadv_get_hardif_from_info() - Retrieve hardif from genl attributes
> > + * @bat_priv: the bat priv with all the soft interface information
> > + * @net: the applicable net namespace
> > + * @info: receiver information
> > + *
> > + * Return: Pointer to hard interface on success, error pointer on error
> > + */
> > +static struct batadv_hard_iface *
> > +batadv_get_hardif_from_info(struct batadv_priv *bat_priv, struct net 
*net,
> > +			    struct genl_info *info)
> > +{
> > +	struct batadv_hard_iface *hard_iface;
> > +	struct net_device *hard_dev;
> > +	unsigned int hardif_index;
> > +
> > +	if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
> > +
> > +	hard_dev = dev_get_by_index(net, hardif_index);
> > +	if (!hard_dev)
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	hard_iface = batadv_hardif_get_by_netdev(hard_dev);
> > +	if (!hard_iface)
> > +		goto err_put_harddev;
> > +
> > +	if (hard_iface->soft_iface != bat_priv->soft_iface)
> > +		goto err_put_hardif;
> 
> When would this case above happen?


When you ask for a hard interface from a different mesh interface or a hard 
interface without a mesh interface.


> It seems unnecessary to keep holding a reference to hard_dev on
> successful return here (and releasing it in post_doit).
[...]

Ok.

> often with these net_device reference counters...) Same for
> batadv_get_softif_from_info().

No, no, no, no, no, no, no and again no. The net_device is the only thing with 
a refcounter here. The bat_priv doesn't have a refcounter because it is just a 
memory region in the net_device.

Kind regards,
	Sven
  
Linus Lüssing Jan. 5, 2019, 3:12 p.m. UTC | #5
On Fri, Jan 04, 2019 at 08:52:22AM +0100, Sven Eckelmann wrote:
> > It seems unnecessary to keep holding a reference to hard_dev on
> > successful return here (and releasing it in post_doit).
> [...]
> 
> Ok.
> 
> > often with these net_device reference counters...) Same for
> > batadv_get_softif_from_info().
> 
> No, no, no, no, no, no, no and again no. The net_device is the only thing with 
> a refcounter here. The bat_priv doesn't have a refcounter because it is just a 
> memory region in the net_device.

With "Same for batadv_get_softif_from_info()" I was talking about
the

> In any case, maybe it would make sense to mention increased refcounts in
> the kerneldoc?

That is to add a line like "Increases the refcount of foo and
bar." So that users know which references to release and how to
use this function just from reading the kerneldoc.

I'm not talking about bat_priv here.
  
Sven Eckelmann Jan. 5, 2019, 5:22 p.m. UTC | #6
On Saturday, 5 January 2019 16.12.24 CET Linus Lüssing wrote:
[...]
> > > often with these net_device reference counters...) Same for
> > > batadv_get_softif_from_info().
> > 
> > No, no, no, no, no, no, no and again no. The net_device is the only thing with 
> > a refcounter here. The bat_priv doesn't have a refcounter because it is just a 
> > memory region in the net_device.
> 
> With "Same for batadv_get_softif_from_info()" I was talking about
> the
> 
> > In any case, maybe it would make sense to mention increased refcounts in
> > the kerneldoc?
> 
> That is to add a line like "Increases the refcount of foo and
> bar." So that users know which references to release and how to
> use this function just from reading the kerneldoc.
> 
> I'm not talking about bat_priv here.

Ok

Kind regards,
	Sven
  

Patch

diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h
index 2d6a175e..eb5097df 100644
--- a/include/uapi/linux/batman_adv.h
+++ b/include/uapi/linux/batman_adv.h
@@ -398,9 +398,15 @@  enum batadv_nl_commands {
 	BATADV_CMD_GET_ROUTING_ALGOS,
 
 	/**
-	 * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces
+	 * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
+	 *  current softif
 	 */
-	BATADV_CMD_GET_HARDIFS,
+	BATADV_CMD_GET_HARDIF,
+
+	/**
+	 * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
+	 */
+	BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
 
 	/**
 	 * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
@@ -453,6 +459,12 @@  enum batadv_nl_commands {
 	 */
 	BATADV_CMD_SET_MESH,
 
+	/**
+	 * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
+	 *  current softif
+	 */
+	BATADV_CMD_SET_HARDIF,
+
 	/* add new commands above here */
 
 	/**
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index d89761f8..68116eb8 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -21,6 +21,7 @@ 
 
 #include <linux/atomic.h>
 #include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/cache.h>
 #include <linux/err.h>
@@ -76,6 +77,13 @@  enum batadv_genl_ops_flags {
 	 *  safed in info->user_ptr[0]
 	 */
 	BATADV_FLAG_NEED_MESH = BIT(0),
+
+	/**
+	 * @BATADV_FLAG_NEED_HARDIF: request requires valid hard interface in
+	 *  attribute BATADV_ATTR_HARD_IFINDEX and expects a pointer to it to be
+	 *  safed in info->user_ptr[1]
+	 */
+	BATADV_FLAG_NEED_HARDIF = BIT(1),
 };
 
 static const struct genl_multicast_group batadv_netlink_mcgrps[] = {
@@ -445,29 +453,38 @@  batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
 }
 
 /**
- * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message
+ * batadv_netlink_hardif_put() - Fill message with hardif attributes
  * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hard_iface: hard interface which was modified
+ * @cmd: type of message to generate
  * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
  * @cb: Control block containing additional options
- * @hard_iface: Hard interface to dump
  *
- * Return: error code, or 0 on success
+ * Return: 0 on success or negative error number in case of failure
  */
-static int
-batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
-				 struct netlink_callback *cb,
-				 struct batadv_hard_iface *hard_iface)
+static int batadv_netlink_hardif_put(struct sk_buff *msg,
+				     struct batadv_priv *bat_priv,
+				     struct batadv_hard_iface *hard_iface,
+				     enum batadv_nl_commands cmd,
+				     u32 portid, u32 seq, int flags,
+				     struct netlink_callback *cb)
 {
 	struct net_device *net_dev = hard_iface->net_dev;
 	void *hdr;
 
-	hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
-			  &batadv_netlink_family, NLM_F_MULTI,
-			  BATADV_CMD_GET_HARDIFS);
+	hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
 	if (!hdr)
-		return -EMSGSIZE;
+		return -ENOBUFS;
+
+	if (cb)
+		genl_dump_check_consistent(cb, hdr);
 
-	genl_dump_check_consistent(cb, hdr);
+	if (nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX,
+			bat_priv->soft_iface->ifindex))
+		goto nla_put_failure;
 
 	if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
 			net_dev->ifindex) ||
@@ -485,24 +502,107 @@  batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
 	genlmsg_end(msg, hdr);
 	return 0;
 
- nla_put_failure:
+nla_put_failure:
 	genlmsg_cancel(msg, hdr);
 	return -EMSGSIZE;
 }
 
 /**
- * batadv_netlink_dump_hardifs() - Dump all hard interface into a messages
+ * batadv_netlink_notify_hardif() - send hardif attributes to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hard_iface: hard interface which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+static int batadv_netlink_notify_hardif(struct batadv_priv *bat_priv,
+					struct batadv_hard_iface *hard_iface)
+{
+	struct sk_buff *msg;
+	int ret;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	ret = batadv_netlink_hardif_put(msg, bat_priv, hard_iface,
+					BATADV_CMD_SET_HARDIF, 0, 0, 0, NULL);
+	if (ret < 0) {
+		nlmsg_free(msg);
+		return ret;
+	}
+
+	genlmsg_multicast_netns(&batadv_netlink_family,
+				dev_net(bat_priv->soft_iface), msg, 0,
+				BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+	return 0;
+}
+
+/**
+ * batadv_netlink_get_hardif() - Get hardif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_get_hardif(struct sk_buff *skb,
+				     struct genl_info *info)
+{
+	struct batadv_hard_iface *hard_iface = info->user_ptr[1];
+	struct batadv_priv *bat_priv = info->user_ptr[0];
+	struct sk_buff *msg;
+	int ret;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	ret = batadv_netlink_hardif_put(msg, bat_priv, hard_iface,
+					BATADV_CMD_GET_HARDIF,
+					info->snd_portid, info->snd_seq, 0,
+					NULL);
+	if (ret < 0) {
+		nlmsg_free(msg);
+		return ret;
+	}
+
+	ret = genlmsg_reply(msg, info);
+
+	return ret;
+}
+
+/**
+ * batadv_netlink_set_hardif() - Set hardif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_set_hardif(struct sk_buff *skb,
+				     struct genl_info *info)
+{
+	struct batadv_hard_iface *hard_iface = info->user_ptr[1];
+	struct batadv_priv *bat_priv = info->user_ptr[0];
+
+	batadv_netlink_notify_hardif(bat_priv, hard_iface);
+
+	return 0;
+}
+
+/**
+ * batadv_netlink_dump_hardif() - Dump all hard interface into a messages
  * @msg: Netlink message to dump into
  * @cb: Parameters from query
  *
  * Return: error code, or length of reply message on success
  */
 static int
-batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
+batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
 {
 	struct net *net = sock_net(cb->skb->sk);
 	struct net_device *soft_iface;
 	struct batadv_hard_iface *hard_iface;
+	struct batadv_priv *bat_priv;
 	int ifindex;
 	int portid = NETLINK_CB(cb->skb).portid;
 	int skip = cb->args[0];
@@ -522,6 +622,8 @@  batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
 		return -ENODEV;
 	}
 
+	bat_priv = netdev_priv(soft_iface);
+
 	rtnl_lock();
 	cb->seq = batadv_hardif_generation << 1 | 1;
 
@@ -532,8 +634,10 @@  batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
 		if (i++ < skip)
 			continue;
 
-		if (batadv_netlink_dump_hardif_entry(msg, portid, cb,
-						     hard_iface)) {
+		if (batadv_netlink_hardif_put(msg, bat_priv, hard_iface,
+					      BATADV_CMD_GET_HARDIF,
+					      portid, cb->nlh->nlmsg_seq,
+					      NLM_F_MULTI, cb)) {
 			i--;
 			break;
 		}
@@ -581,6 +685,48 @@  batadv_get_softif_from_info(struct net *net, struct genl_info *info)
 	return ERR_PTR(-EINVAL);
 }
 
+/**
+ * batadv_get_hardif_from_info() - Retrieve hardif from genl attributes
+ * @bat_priv: the bat priv with all the soft interface information
+ * @net: the applicable net namespace
+ * @info: receiver information
+ *
+ * Return: Pointer to hard interface on success, error pointer on error
+ */
+static struct batadv_hard_iface *
+batadv_get_hardif_from_info(struct batadv_priv *bat_priv, struct net *net,
+			    struct genl_info *info)
+{
+	struct batadv_hard_iface *hard_iface;
+	struct net_device *hard_dev;
+	unsigned int hardif_index;
+
+	if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
+		return ERR_PTR(-EINVAL);
+
+	hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
+
+	hard_dev = dev_get_by_index(net, hardif_index);
+	if (!hard_dev)
+		return ERR_PTR(-ENODEV);
+
+	hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+	if (!hard_iface)
+		goto err_put_harddev;
+
+	if (hard_iface->soft_iface != bat_priv->soft_iface)
+		goto err_put_hardif;
+
+	return hard_iface;
+
+err_put_hardif:
+	batadv_hardif_put(hard_iface);
+err_put_harddev:
+	dev_put(hard_dev);
+
+	return ERR_PTR(-EINVAL);
+}
+
 /**
  * batadv_pre_doit() - Prepare batman-adv genl doit request
  * @ops: requested netlink operation
@@ -592,7 +738,16 @@  batadv_get_softif_from_info(struct net *net, struct genl_info *info)
 static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 			   struct genl_info *info)
 {
-	struct batadv_priv *bat_priv;
+	struct batadv_hard_iface *hard_iface;
+	struct batadv_priv *bat_priv = NULL;
+	int ret;
+
+	if (WARN_ON(ops->internal_flags & BATADV_FLAG_NEED_HARDIF))
+		return -EINVAL;
+
+	if (WARN_ON((ops->internal_flags & BATADV_FLAG_NEED_HARDIF) &&
+		    (~ops->internal_flags & BATADV_FLAG_NEED_MESH)))
+		return -EINVAL;
 
 	if (ops->internal_flags & BATADV_FLAG_NEED_MESH) {
 		bat_priv = batadv_get_softif_from_info(genl_info_net(info),
@@ -603,7 +758,25 @@  static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 		info->user_ptr[0] = bat_priv;
 	}
 
+	if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF) {
+		hard_iface = batadv_get_hardif_from_info(bat_priv,
+							 genl_info_net(info),
+							 info);
+		if (IS_ERR(hard_iface)) {
+			ret = PTR_ERR(hard_iface);
+			goto err_put_softif;
+		}
+
+		info->user_ptr[1] = hard_iface;
+	}
+
 	return 0;
+
+err_put_softif:
+	if (bat_priv)
+		dev_put(bat_priv->soft_iface);
+
+	return ret;
 }
 
 /**
@@ -615,7 +788,18 @@  static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 static void batadv_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
 			     struct genl_info *info)
 {
+	struct batadv_hard_iface *hard_iface;
 	struct batadv_priv *bat_priv;
+	struct net_device *hard_dev;
+
+	if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF &&
+	    info->user_ptr[1]) {
+		hard_iface = info->user_ptr[1];
+		hard_dev = hard_iface->net_dev;
+
+		batadv_hardif_put(hard_iface);
+		dev_put(hard_dev);
+	}
 
 	if (ops->internal_flags & BATADV_FLAG_NEED_MESH && info->user_ptr[0]) {
 		bat_priv = info->user_ptr[0];
@@ -652,10 +836,13 @@  static const struct genl_ops batadv_netlink_ops[] = {
 		.dumpit = batadv_algo_dump,
 	},
 	{
-		.cmd = BATADV_CMD_GET_HARDIFS,
-		.flags = GENL_ADMIN_PERM,
+		.cmd = BATADV_CMD_GET_HARDIF,
+		/* can be retrieved by unprivileged users */
 		.policy = batadv_netlink_policy,
-		.dumpit = batadv_netlink_dump_hardifs,
+		.dumpit = batadv_netlink_dump_hardif,
+		.doit = batadv_netlink_get_hardif,
+		.internal_flags = BATADV_FLAG_NEED_MESH |
+				  BATADV_FLAG_NEED_HARDIF,
 	},
 	{
 		.cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL,
@@ -718,6 +905,14 @@  static const struct genl_ops batadv_netlink_ops[] = {
 		.doit = batadv_netlink_set_mesh,
 		.internal_flags = BATADV_FLAG_NEED_MESH,
 	},
+	{
+		.cmd = BATADV_CMD_SET_HARDIF,
+		.flags = GENL_ADMIN_PERM,
+		.policy = batadv_netlink_policy,
+		.doit = batadv_netlink_set_hardif,
+		.internal_flags = BATADV_FLAG_NEED_MESH |
+				  BATADV_FLAG_NEED_HARDIF,
+	},
 };
 
 struct genl_family batadv_netlink_family __ro_after_init = {