@@ -27,6 +27,7 @@
#define BATADV_NL_NAME "batadv"
+#define BATADV_NL_MCAST_GROUP_CONFIG "config"
#define BATADV_NL_MCAST_GROUP_TPMETER "tpmeter"
/**
@@ -344,6 +345,26 @@ enum batadv_nl_attrs {
*/
BATADV_ATTR_MCAST_FLAGS_PRIV,
+ /**
+ * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+ */
+ BATADV_ATTR_VLANID,
+
+ /**
+ * @BATADV_ATTR_OPTION_NAME: name of option to modify
+ */
+ BATADV_ATTR_OPTION_NAME,
+
+ /**
+ * @BATADV_ATTR_OPTION_TYPE: type of @BATADV_ATTR_OPTION_VALUE
+ */
+ BATADV_ATTR_OPTION_TYPE,
+
+ /**
+ * @BATADV_ATTR_OPTION_VALUE: value set for @BATADV_ATTR_OPTION_NAME
+ */
+ BATADV_ATTR_OPTION_VALUE,
+
/* add attributes above here, update the policy in netlink.c */
/**
@@ -443,6 +464,40 @@ enum batadv_nl_commands {
*/
BATADV_CMD_GET_MCAST_FLAGS,
+ /**
+ * @BATADV_CMD_GET_OPTION: Get option(s) from softif
+ */
+ BATADV_CMD_GET_OPTION,
+
+ /**
+ * @BATADV_CMD_SET_OPTION: Set option for softif
+ */
+ BATADV_CMD_SET_OPTION,
+
+ /**
+ * @BATADV_CMD_GET_OPTION_HARDIF: Get option(s) from a hardif of the
+ * current softif
+ */
+ BATADV_CMD_GET_OPTION_HARDIF,
+
+ /**
+ * @BATADV_CMD_SET_OPTION_HARDIF: Set option for hardif of the
+ * current softif
+ */
+ BATADV_CMD_SET_OPTION_HARDIF,
+
+ /**
+ * @BATADV_CMD_GET_OPTION_VLAN: Get option(s) from a VLAN of the
+ * current softif
+ */
+ BATADV_CMD_GET_OPTION_VLAN,
+
+ /**
+ * @BATADV_CMD_SET_OPTION_VLAN: Set option for VLAN of the
+ * current softif
+ */
+ BATADV_CMD_SET_OPTION_VLAN,
+
/* add new commands above here */
/**
@@ -36,6 +36,7 @@ batman-adv-$(CONFIG_BATMAN_ADV_DEBUG) += log.o
batman-adv-y += main.o
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
batman-adv-y += netlink.o
+batman-adv-y += netlink_cfg.o
batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
batman-adv-y += originator.o
batman-adv-y += routing.o
@@ -49,6 +49,7 @@
#include "gateway_client.h"
#include "hard-interface.h"
#include "multicast.h"
+#include "netlink_cfg.h"
#include "originator.h"
#include "soft-interface.h"
#include "tp_meter.h"
@@ -56,12 +57,8 @@
struct genl_family batadv_netlink_family;
-/* multicast groups */
-enum batadv_netlink_multicast_groups {
- BATADV_NL_MCGRP_TPMETER,
-};
-
static const struct genl_multicast_group batadv_netlink_mcgrps[] = {
+ [BATADV_NL_MCGRP_CONFIG] = { .name = BATADV_NL_MCAST_GROUP_CONFIG },
[BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
};
@@ -104,6 +101,9 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
[BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 },
[BATADV_ATTR_MCAST_FLAGS] = { .type = NLA_U32 },
[BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NLA_U32 },
+ [BATADV_ATTR_VLANID] = { .type = NLA_U16 },
+ [BATADV_ATTR_OPTION_NAME] = { .type = NLA_NUL_STRING },
+ [BATADV_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
};
/**
@@ -630,7 +630,45 @@ static const struct genl_ops batadv_netlink_ops[] = {
.policy = batadv_netlink_policy,
.dumpit = batadv_mcast_flags_dump,
},
-
+ {
+ .cmd = BATADV_CMD_GET_OPTION,
+ /* can be retrieved by unprivileged users */
+ .policy = batadv_netlink_policy,
+ .dumpit = batadv_get_option_dump,
+ .doit = batadv_get_option,
+ },
+ {
+ .cmd = BATADV_CMD_SET_OPTION,
+ .flags = GENL_ADMIN_PERM,
+ .policy = batadv_netlink_policy,
+ .doit = batadv_set_option,
+ },
+ {
+ .cmd = BATADV_CMD_GET_OPTION_HARDIF,
+ /* can be retrieved by unprivileged users */
+ .policy = batadv_netlink_policy,
+ .dumpit = batadv_get_option_hardif_dump,
+ .doit = batadv_get_option_hardif,
+ },
+ {
+ .cmd = BATADV_CMD_SET_OPTION_HARDIF,
+ .flags = GENL_ADMIN_PERM,
+ .policy = batadv_netlink_policy,
+ .doit = batadv_set_option_hardif,
+ },
+ {
+ .cmd = BATADV_CMD_GET_OPTION_VLAN,
+ /* can be retrieved by unprivileged users */
+ .policy = batadv_netlink_policy,
+ .dumpit = batadv_get_option_vlan_dump,
+ .doit = batadv_get_option_vlan,
+ },
+ {
+ .cmd = BATADV_CMD_SET_OPTION_VLAN,
+ .flags = GENL_ADMIN_PERM,
+ .policy = batadv_netlink_policy,
+ .doit = batadv_set_option_vlan,
+ },
};
struct genl_family batadv_netlink_family __ro_after_init = {
@@ -26,6 +26,12 @@
struct nlmsghdr;
+/* multicast groups */
+enum batadv_netlink_multicast_groups {
+ BATADV_NL_MCGRP_CONFIG,
+ BATADV_NL_MCGRP_TPMETER,
+};
+
void batadv_netlink_register(void);
void batadv_netlink_unregister(void);
int batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype);
new file mode 100644
@@ -0,0 +1,1051 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors:
+ *
+ * Sven Eckelmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "netlink_cfg.h"
+#include "main.h"
+
+#include <linux/errno.h>
+#include <linux/genetlink.h>
+#include <linux/gfp.h>
+#include <linux/if_vlan.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+#include <uapi/linux/batadv_packet.h>
+#include <uapi/linux/batman_adv.h>
+
+#include "hard-interface.h"
+#include "netlink.h"
+#include "soft-interface.h"
+
+static const struct batadv_option softif_options[] = {
+};
+
+static const struct batadv_option hardif_options[] = {
+};
+
+static const struct batadv_option vlan_options[] = {
+};
+
+/**
+ * batadv_find_option() - Search for option with the correct name
+ * @name: name of the option to find
+ * @options: array of available options
+ * @options_num: number of entries in @options
+ *
+ * Return: pointer to option on success or NULL in case of failure
+ */
+static const struct batadv_option *
+batadv_find_option(const char *name, const struct batadv_option options[],
+ size_t options_num)
+{
+ size_t i;
+
+ for (i = 0; i < options_num; i++) {
+ if (strcmp(options[i].name, name) != 0)
+ continue;
+
+ return &options[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * batadv_option_value_get_from_info() - Validate and extract value from msg
+ * @option: option to extract value for
+ * @info: generic netlink info with attributes
+ * @val: Target to save value
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_option_value_get_from_info(const struct batadv_option *option,
+ struct genl_info *info,
+ union batadv_config_value *val)
+{
+ struct nlattr *nla = info->attrs[BATADV_ATTR_OPTION_VALUE];
+ int attrlen;
+ int minlen;
+
+ if (option->type != NLA_FLAG && !nla)
+ return -EINVAL;
+
+ switch (option->type) {
+ case NLA_U8:
+ attrlen = nla_len(nla);
+ if (attrlen < sizeof(u8))
+ return -EINVAL;
+
+ val->vu8 = nla_get_u8(nla);
+ break;
+ case NLA_U16:
+ attrlen = nla_len(nla);
+ if (attrlen < sizeof(u16))
+ return -EINVAL;
+
+ val->vu16 = nla_get_u16(nla);
+ break;
+ case NLA_U32:
+ attrlen = nla_len(nla);
+ if (attrlen < sizeof(u32))
+ return -EINVAL;
+
+ val->vu32 = nla_get_u32(nla);
+ break;
+ case NLA_NUL_STRING:
+ attrlen = nla_len(nla);
+ minlen = min_t(int, __BATADV_PARAM_MAX_STRING_VALUE, attrlen);
+ if (!minlen || !memchr(nla_data(nla), '\0', minlen))
+ return -EINVAL;
+
+ strlcpy(val->string, nla_data(nla), sizeof(val->string));
+ break;
+ case NLA_FLAG:
+ val->vbool = !!nla;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * batadv_get_option_fill_open() - Fill message with option data and keep it
+ * open for further attributes
+ * @msg: Netlink message to dump into
+ * @hdr: storage position for header
+ * @bat_priv: the bat priv with all the soft interface information
+ * @option: option to dump
+ * @ext_arg: Additional option argument
+ * @cmd: type of message to generate
+ * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_get_option_fill_open(struct sk_buff *msg, void **hdr,
+ struct batadv_priv *bat_priv,
+ const struct batadv_option *option,
+ void *ext_arg,
+ enum batadv_nl_commands cmd,
+ u32 portid, u32 seq, int flags)
+{
+ union batadv_config_value val;
+ int ret;
+
+ ret = option->get(bat_priv, ext_arg, &val);
+ if (ret < 0)
+ return ret;
+
+ *hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags,
+ cmd);
+ if (!*hdr)
+ return -EMSGSIZE;
+
+ if (nla_put_string(msg, BATADV_ATTR_OPTION_NAME, option->name))
+ return -EMSGSIZE;
+
+ if (nla_put_u8(msg, BATADV_ATTR_OPTION_TYPE, option->type))
+ return -EMSGSIZE;
+
+ switch (option->type) {
+ case NLA_U8:
+ if (nla_put_u8(msg, BATADV_ATTR_OPTION_VALUE, val.vu8))
+ return -EMSGSIZE;
+ break;
+ case NLA_U16:
+ if (nla_put_u16(msg, BATADV_ATTR_OPTION_VALUE, val.vu16))
+ return -EMSGSIZE;
+ break;
+ case NLA_U32:
+ if (nla_put_u32(msg, BATADV_ATTR_OPTION_VALUE, val.vu32))
+ return -EMSGSIZE;
+ break;
+ case NLA_NUL_STRING:
+ if (nla_put_string(msg, BATADV_ATTR_OPTION_VALUE, val.string))
+ return -EMSGSIZE;
+ break;
+ case NLA_FLAG:
+ if (val.vbool && nla_put_flag(msg, BATADV_ATTR_OPTION_VALUE))
+ return -EMSGSIZE;
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * batadv_get_option_fill() - Fill message with option data
+ * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
+ * @option: option to dump
+ * @ext_arg: Additional option argument
+ * @cmd: type of message to generate
+ * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_get_option_fill(struct sk_buff *msg,
+ struct batadv_priv *bat_priv,
+ const struct batadv_option *option,
+ void *ext_arg, enum batadv_nl_commands cmd,
+ u32 portid, u32 seq, int flags)
+{
+ void *hdr;
+ int ret;
+
+ ret = batadv_get_option_fill_open(msg, &hdr, bat_priv, option, ext_arg,
+ cmd, portid, seq, flags);
+ if (ret < 0)
+ return ret;
+
+ genlmsg_end(msg, hdr);
+ return 0;
+}
+
+/**
+ * batadv_option_notify() - send new option value to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @option: option which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+static int batadv_option_notify(struct batadv_priv *bat_priv,
+ const struct batadv_option *option)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int ret;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = batadv_get_option_fill_open(msg, &hdr, bat_priv, option, NULL,
+ BATADV_CMD_SET_OPTION, 0, 0, 0);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX,
+ bat_priv->soft_iface->ifindex))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&batadv_netlink_family,
+ dev_net(bat_priv->soft_iface), msg, 0,
+ BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ ret = -EMSGSIZE;
+ nlmsg_free(msg);
+ return ret;
+}
+
+/**
+ * batadv_get_option() - Get softif option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_get_option(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ const struct batadv_option *option;
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ const char *option_name;
+ struct sk_buff *msg;
+ int ifindex;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, softif_options,
+ ARRAY_SIZE(softif_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto err_put_softif;
+ }
+
+ ret = batadv_get_option_fill(msg, bat_priv, option,
+ NULL, BATADV_CMD_GET_OPTION,
+ info->snd_portid, info->snd_seq, 0);
+ if (ret < 0) {
+ nlmsg_free(msg);
+ goto err_put_softif;
+ }
+
+ ret = genlmsg_reply(msg, info);
+
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_set_option() - Set softif option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_set_option(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ const struct batadv_option *option;
+ struct net_device *soft_iface;
+ union batadv_config_value val;
+ struct batadv_priv *bat_priv;
+ const char *option_name;
+ int option_type;
+ int ifindex;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_TYPE])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, softif_options,
+ ARRAY_SIZE(softif_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ option_type = nla_get_u8(info->attrs[BATADV_ATTR_OPTION_TYPE]);
+ if (option_type != option->type)
+ return -EINVAL;
+
+ ret = batadv_option_value_get_from_info(option, info, &val);
+ if (ret < 0)
+ return ret;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ ret = option->set(bat_priv, NULL, &val);
+ if (ret < 0)
+ goto err_put_softif;
+
+ batadv_option_notify(bat_priv, option);
+
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_get_option_dump() - Dump softif options into a message
+ * @msg: Netlink message to dump into
+ * @cb: Control block containing additional options
+ *
+ * Return: Error code, or length of message
+ */
+int batadv_get_option_dump(struct sk_buff *msg, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(cb->skb->sk);
+ unsigned int start = cb->args[0];
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ unsigned int i;
+ int ifindex;
+ int ret;
+
+ ifindex = batadv_netlink_get_ifindex(cb->nlh,
+ BATADV_ATTR_MESH_IFINDEX);
+ if (!ifindex)
+ return -EINVAL;
+
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ for (i = start; i < ARRAY_SIZE(softif_options); i++) {
+ ret = batadv_get_option_fill(msg, bat_priv, &softif_options[i],
+ NULL, BATADV_CMD_GET_OPTION,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI);
+ if (ret == -EOPNOTSUPP)
+ continue;
+ if (ret < 0)
+ break;
+ }
+
+ cb->args[0] = i;
+ ret = msg->len;
+
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_option_hardif_notify() - send new hardif option value to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hard_iface: hard interface which was modified
+ * @option: option which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+static int batadv_option_hardif_notify(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *hard_iface,
+ const struct batadv_option *option)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int ret;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = batadv_get_option_fill_open(msg, &hdr, bat_priv, option,
+ hard_iface,
+ BATADV_CMD_SET_OPTION_HARDIF, 0, 0,
+ 0);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ 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,
+ hard_iface->net_dev->ifindex))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&batadv_netlink_family,
+ dev_net(bat_priv->soft_iface), msg, 0,
+ BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ ret = -EMSGSIZE;
+ nlmsg_free(msg);
+ return ret;
+}
+
+/**
+ * batadv_get_option_hardif() - Get hardif option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_get_option_hardif(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct batadv_hard_iface *hard_iface;
+ const struct batadv_option *option;
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ struct net_device *hard_dev;
+ unsigned int hardif_index;
+ const char *option_name;
+ struct sk_buff *msg;
+ int ifindex;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, hardif_options,
+ ARRAY_SIZE(hardif_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
+ hard_dev = dev_get_by_index(net, hardif_index);
+ if (!hard_dev) {
+ ret = -ENODEV;
+ goto err_put_softif;
+ }
+
+ hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ if (!hard_iface) {
+ ret = -EINVAL;
+ goto err_put_harddev;
+ }
+
+ if (hard_iface->soft_iface != soft_iface) {
+ ret = -EINVAL;
+ goto err_put_hardif;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto err_put_hardif;
+ }
+
+ ret = batadv_get_option_fill(msg, bat_priv, option, hard_iface,
+ BATADV_CMD_GET_OPTION_HARDIF,
+ info->snd_portid, info->snd_seq, 0);
+ if (ret < 0) {
+ nlmsg_free(msg);
+ goto err_put_hardif;
+ }
+
+ ret = genlmsg_reply(msg, info);
+
+err_put_hardif:
+ batadv_hardif_put(hard_iface);
+err_put_harddev:
+ dev_put(hard_dev);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_set_option_hardif() - Set hardif option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_set_option_hardif(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct batadv_hard_iface *hard_iface;
+ const struct batadv_option *option;
+ struct net_device *soft_iface;
+ union batadv_config_value val;
+ struct batadv_priv *bat_priv;
+ struct net_device *hard_dev;
+ unsigned int hardif_index;
+ const char *option_name;
+ int option_type;
+ int ifindex;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_TYPE])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, hardif_options,
+ ARRAY_SIZE(hardif_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ option_type = nla_get_u8(info->attrs[BATADV_ATTR_OPTION_TYPE]);
+ if (option_type != option->type)
+ return -EINVAL;
+
+ ret = batadv_option_value_get_from_info(option, info, &val);
+ if (ret < 0)
+ return ret;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
+ hard_dev = dev_get_by_index(net, hardif_index);
+ if (!hard_dev) {
+ ret = -ENODEV;
+ goto err_put_softif;
+ }
+
+ hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ if (!hard_iface) {
+ ret = -EINVAL;
+ goto err_put_harddev;
+ }
+
+ if (hard_iface->soft_iface != soft_iface) {
+ ret = -EINVAL;
+ goto err_put_hardif;
+ }
+
+ ret = option->set(bat_priv, hard_iface, &val);
+ if (ret < 0)
+ goto err_put_hardif;
+
+ batadv_option_hardif_notify(bat_priv, hard_iface, option);
+
+err_put_hardif:
+ batadv_hardif_put(hard_iface);
+err_put_harddev:
+ dev_put(hard_dev);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_get_option_hardif_dump() - Dump hardif options into a message
+ * @msg: Netlink message to dump into
+ * @cb: Control block containing additional options
+ *
+ * Return: Error code, or length of message
+ */
+int batadv_get_option_hardif_dump(struct sk_buff *msg,
+ struct netlink_callback *cb)
+{
+ struct net *net = sock_net(cb->skb->sk);
+ struct batadv_hard_iface *hard_iface;
+ unsigned int start = cb->args[0];
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ struct net_device *hard_dev;
+ unsigned int hardif_index;
+ unsigned int i;
+ int ifindex;
+ int ret;
+
+ ifindex = batadv_netlink_get_ifindex(cb->nlh,
+ BATADV_ATTR_MESH_IFINDEX);
+ if (!ifindex)
+ return -EINVAL;
+
+ hardif_index = batadv_netlink_get_ifindex(cb->nlh,
+ BATADV_ATTR_HARD_IFINDEX);
+ if (!hardif_index)
+ return -EINVAL;
+
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ hard_dev = dev_get_by_index(net, hardif_index);
+ if (!hard_dev) {
+ ret = -ENODEV;
+ goto err_put_softif;
+ }
+
+ hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ if (!hard_iface) {
+ ret = -EINVAL;
+ goto err_put_harddev;
+ }
+
+ if (hard_iface->soft_iface != soft_iface) {
+ ret = -EINVAL;
+ goto err_put_hardif;
+ }
+
+ for (i = start; i < ARRAY_SIZE(hardif_options); i++) {
+ ret = batadv_get_option_fill(msg, bat_priv, &hardif_options[i],
+ hard_iface,
+ BATADV_CMD_GET_OPTION_HARDIF,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI);
+ if (ret == -EOPNOTSUPP)
+ continue;
+ if (ret < 0)
+ break;
+ }
+
+ cb->args[0] = i;
+ ret = msg->len;
+
+err_put_hardif:
+ batadv_hardif_put(hard_iface);
+err_put_harddev:
+ dev_put(hard_dev);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_option_vlan_notify() - send new vlan option value to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: vlan which was modified
+ * @option: option which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+static int batadv_option_vlan_notify(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan,
+ const struct batadv_option *option)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int ret;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = batadv_get_option_fill_open(msg, &hdr, bat_priv, option, vlan,
+ BATADV_CMD_SET_OPTION_VLAN, 0, 0, 0);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ 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_VLANID, vlan->vid & VLAN_VID_MASK))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&batadv_netlink_family,
+ dev_net(bat_priv->soft_iface), msg, 0,
+ BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ ret = -EMSGSIZE;
+ nlmsg_free(msg);
+ return ret;
+}
+
+/**
+ * batadv_get_option_vlan() - Get vlan option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: Error code, or length of message
+ */
+int batadv_get_option_vlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ const struct batadv_option *option;
+ struct batadv_softif_vlan *vlan;
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ const char *option_name;
+ struct sk_buff *msg;
+ int ifindex;
+ u16 vid;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_VLANID])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, vlan_options,
+ ARRAY_SIZE(vlan_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ vid = nla_get_u16(info->attrs[BATADV_ATTR_VLANID]);
+ vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+ if (!vlan) {
+ ret = -ENOENT;
+ goto err_put_softif;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto err_put_vlan;
+ }
+
+ ret = batadv_get_option_fill(msg, bat_priv, option,
+ vlan, BATADV_CMD_GET_OPTION_VLAN,
+ info->snd_portid, info->snd_seq, 0);
+ if (ret < 0) {
+ nlmsg_free(msg);
+ goto err_put_vlan;
+ }
+
+ ret = genlmsg_reply(msg, info);
+
+err_put_vlan:
+ batadv_softif_vlan_put(vlan);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_set_option_vlan() - Get vlan option
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_set_option_vlan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ const struct batadv_option *option;
+ struct batadv_softif_vlan *vlan;
+ struct net_device *soft_iface;
+ union batadv_config_value val;
+ struct batadv_priv *bat_priv;
+ const char *option_name;
+ int option_type;
+ int ifindex;
+ u16 vid;
+ int ret;
+
+ if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_VLANID])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_NAME])
+ return -EINVAL;
+
+ if (!info->attrs[BATADV_ATTR_OPTION_TYPE])
+ return -EINVAL;
+
+ option_name = nla_data(info->attrs[BATADV_ATTR_OPTION_NAME]);
+ option = batadv_find_option(option_name, vlan_options,
+ ARRAY_SIZE(vlan_options));
+ if (!option)
+ return -EOPNOTSUPP;
+
+ option_type = nla_get_u8(info->attrs[BATADV_ATTR_OPTION_TYPE]);
+ if (option_type != option->type)
+ return -EINVAL;
+
+ ret = batadv_option_value_get_from_info(option, info, &val);
+ if (ret < 0)
+ return ret;
+
+ ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ vid = nla_get_u16(info->attrs[BATADV_ATTR_VLANID]);
+ vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+ if (!vlan) {
+ ret = -ENOENT;
+ goto err_put_softif;
+ }
+
+ ret = option->set(bat_priv, vlan, &val);
+ if (ret < 0)
+ goto err_put_vlan;
+
+ batadv_option_vlan_notify(bat_priv, vlan, option);
+
+err_put_vlan:
+ batadv_softif_vlan_put(vlan);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
+
+/**
+ * batadv_get_option_vlan_dump() - Dump vlan options into a message
+ * @msg: Netlink message to dump into
+ * @cb: Control block containing additional options
+ *
+ * Return: Error code, or length of message
+ */
+int batadv_get_option_vlan_dump(struct sk_buff *msg,
+ struct netlink_callback *cb)
+{
+ struct net *net = sock_net(cb->skb->sk);
+ unsigned int start = cb->args[0];
+ struct batadv_softif_vlan *vlan;
+ struct net_device *soft_iface;
+ struct batadv_priv *bat_priv;
+ struct nlattr *vid_attr;
+ unsigned int i;
+ int ifindex;
+ u16 vid;
+ int ret;
+
+ vid_attr = nlmsg_find_attr(cb->nlh, GENL_HDRLEN, BATADV_ATTR_VLANID);
+ if (!vid_attr)
+ return -EINVAL;
+
+ ifindex = batadv_netlink_get_ifindex(cb->nlh,
+ BATADV_ATTR_MESH_IFINDEX);
+ if (!ifindex)
+ return -EINVAL;
+
+ soft_iface = dev_get_by_index(net, ifindex);
+ if (!soft_iface)
+ return -ENODEV;
+
+ if (!batadv_softif_is_valid(soft_iface)) {
+ ret = -EINVAL;
+ goto err_put_softif;
+ }
+
+ bat_priv = netdev_priv(soft_iface);
+
+ vid = nla_get_u16(vid_attr);
+ vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+ if (!vlan) {
+ ret = -ENOENT;
+ goto err_put_softif;
+ }
+
+ for (i = start; i < ARRAY_SIZE(vlan_options); i++) {
+ ret = batadv_get_option_fill(msg, bat_priv, &vlan_options[i],
+ vlan, BATADV_CMD_GET_OPTION_VLAN,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI);
+ if (ret == -EOPNOTSUPP)
+ continue;
+ if (ret < 0)
+ break;
+ }
+
+ cb->args[0] = i;
+ ret = msg->len;
+
+ batadv_softif_vlan_put(vlan);
+err_put_softif:
+ dev_put(soft_iface);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2010-2018 B.A.T.M.A.N. contributors:
+ *
+ * Sven Eckelmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NET_BATMAN_ADV_NETLINK_CFG_H_
+#define _NET_BATMAN_ADV_NETLINK_CFG_H_
+
+#include "main.h"
+
+#include <linux/types.h>
+
+struct genl_info;
+struct netlink_callback;
+struct netlink_ext_ack;
+struct sk_buff;
+
+#define __BATADV_PARAM_MAX_STRING_VALUE 32
+
+/**
+ * union batadv_config_value - variant storage for batadv_option
+ */
+union batadv_config_value {
+ u8 vu8;
+ u16 vu16;
+ u32 vu32;
+ unsigned int vbool:1;
+ char string[__BATADV_PARAM_MAX_STRING_VALUE];
+};
+
+/**
+ * struct batadv_option - configuration option data
+ * @name: name of option
+ * @type: netlink type of option
+ * @get: get option value
+ * @set: set option value
+ * @validate: (optional) pre-validate input data
+ */
+struct batadv_option {
+ const char *name;
+ int type;
+ int (*get)(struct batadv_priv *bat_priv, void *ext_arg,
+ union batadv_config_value *val);
+ int (*set)(struct batadv_priv *bat_priv, void *ext_arg,
+ const union batadv_config_value *val);
+ int (*validate)(struct batadv_priv *bat_priv, void *ext_arg,
+ const union batadv_config_value *val,
+ struct netlink_ext_ack *extack);
+};
+
+int batadv_get_option(struct sk_buff *skb, struct genl_info *info);
+int batadv_set_option(struct sk_buff *skb, struct genl_info *info);
+int batadv_get_option_dump(struct sk_buff *msg, struct netlink_callback *cb);
+
+int batadv_get_option_hardif(struct sk_buff *skb, struct genl_info *info);
+int batadv_set_option_hardif(struct sk_buff *skb, struct genl_info *info);
+int batadv_get_option_hardif_dump(struct sk_buff *msg,
+ struct netlink_callback *cb);
+
+int batadv_get_option_vlan(struct sk_buff *skb, struct genl_info *info);
+int batadv_set_option_vlan(struct sk_buff *skb, struct genl_info *info);
+int batadv_get_option_vlan_dump(struct sk_buff *msg,
+ struct netlink_callback *cb);
+
+#endif /* _NET_BATMAN_ADV_NETLINK_CFG_H_ */