linux/net/batman-adv/netlink.c
<<
>>
Prefs
   1/* Copyright (C) 2016 B.A.T.M.A.N. contributors:
   2 *
   3 * Matthias Schiffer
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of version 2 of the GNU General Public
   7 * License as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12 * General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18#include "netlink.h"
  19#include "main.h"
  20
  21#include <linux/errno.h>
  22#include <linux/fs.h>
  23#include <linux/genetlink.h>
  24#include <linux/if_ether.h>
  25#include <linux/init.h>
  26#include <linux/netdevice.h>
  27#include <linux/netlink.h>
  28#include <linux/printk.h>
  29#include <linux/stddef.h>
  30#include <linux/types.h>
  31#include <net/genetlink.h>
  32#include <net/netlink.h>
  33#include <uapi/linux/batman_adv.h>
  34
  35#include "hard-interface.h"
  36#include "soft-interface.h"
  37#include "tp_meter.h"
  38
  39struct sk_buff;
  40
  41static struct genl_family batadv_netlink_family = {
  42        .id = GENL_ID_GENERATE,
  43        .hdrsize = 0,
  44        .name = BATADV_NL_NAME,
  45        .version = 1,
  46        .maxattr = BATADV_ATTR_MAX,
  47};
  48
  49/* multicast groups */
  50enum batadv_netlink_multicast_groups {
  51        BATADV_NL_MCGRP_TPMETER,
  52};
  53
  54static struct genl_multicast_group batadv_netlink_mcgrps[] = {
  55        [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
  56};
  57
  58static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
  59        [BATADV_ATTR_VERSION]           = { .type = NLA_STRING },
  60        [BATADV_ATTR_ALGO_NAME]         = { .type = NLA_STRING },
  61        [BATADV_ATTR_MESH_IFINDEX]      = { .type = NLA_U32 },
  62        [BATADV_ATTR_MESH_IFNAME]       = { .type = NLA_STRING },
  63        [BATADV_ATTR_MESH_ADDRESS]      = { .len = ETH_ALEN },
  64        [BATADV_ATTR_HARD_IFINDEX]      = { .type = NLA_U32 },
  65        [BATADV_ATTR_HARD_IFNAME]       = { .type = NLA_STRING },
  66        [BATADV_ATTR_HARD_ADDRESS]      = { .len = ETH_ALEN },
  67        [BATADV_ATTR_ORIG_ADDRESS]      = { .len = ETH_ALEN },
  68        [BATADV_ATTR_TPMETER_RESULT]    = { .type = NLA_U8 },
  69        [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 },
  70        [BATADV_ATTR_TPMETER_BYTES]     = { .type = NLA_U64 },
  71        [BATADV_ATTR_TPMETER_COOKIE]    = { .type = NLA_U32 },
  72};
  73
  74/**
  75 * batadv_netlink_mesh_info_put - fill in generic information about mesh
  76 *  interface
  77 * @msg: netlink message to be sent back
  78 * @soft_iface: interface for which the data should be taken
  79 *
  80 * Return: 0 on success, < 0 on error
  81 */
  82static int
  83batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
  84{
  85        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
  86        struct batadv_hard_iface *primary_if = NULL;
  87        struct net_device *hard_iface;
  88        int ret = -ENOBUFS;
  89
  90        if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
  91            nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
  92                           bat_priv->algo_ops->name) ||
  93            nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
  94            nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
  95            nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
  96                    soft_iface->dev_addr))
  97                goto out;
  98
  99        primary_if = batadv_primary_if_get_selected(bat_priv);
 100        if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
 101                hard_iface = primary_if->net_dev;
 102
 103                if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
 104                                hard_iface->ifindex) ||
 105                    nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
 106                                   hard_iface->name) ||
 107                    nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
 108                            hard_iface->dev_addr))
 109                        goto out;
 110        }
 111
 112        ret = 0;
 113
 114 out:
 115        if (primary_if)
 116                batadv_hardif_put(primary_if);
 117
 118        return ret;
 119}
 120
 121/**
 122 * batadv_netlink_get_mesh_info - handle incoming BATADV_CMD_GET_MESH_INFO
 123 *  netlink request
 124 * @skb: received netlink message
 125 * @info: receiver information
 126 *
 127 * Return: 0 on success, < 0 on error
 128 */
 129static int
 130batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
 131{
 132        struct net *net = genl_info_net(info);
 133        struct net_device *soft_iface;
 134        struct sk_buff *msg = NULL;
 135        void *msg_head;
 136        int ifindex;
 137        int ret;
 138
 139        if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
 140                return -EINVAL;
 141
 142        ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
 143        if (!ifindex)
 144                return -EINVAL;
 145
 146        soft_iface = dev_get_by_index(net, ifindex);
 147        if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
 148                ret = -ENODEV;
 149                goto out;
 150        }
 151
 152        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 153        if (!msg) {
 154                ret = -ENOMEM;
 155                goto out;
 156        }
 157
 158        msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
 159                               &batadv_netlink_family, 0,
 160                               BATADV_CMD_GET_MESH_INFO);
 161        if (!msg_head) {
 162                ret = -ENOBUFS;
 163                goto out;
 164        }
 165
 166        ret = batadv_netlink_mesh_info_put(msg, soft_iface);
 167
 168 out:
 169        if (soft_iface)
 170                dev_put(soft_iface);
 171
 172        if (ret) {
 173                if (msg)
 174                        nlmsg_free(msg);
 175                return ret;
 176        }
 177
 178        genlmsg_end(msg, msg_head);
 179        return genlmsg_reply(msg, info);
 180}
 181
 182/**
 183 * batadv_netlink_tp_meter_put - Fill information of started tp_meter session
 184 * @msg: netlink message to be sent back
 185 * @cookie: tp meter session cookie
 186 *
 187 *  Return: 0 on success, < 0 on error
 188 */
 189static int
 190batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie)
 191{
 192        if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
 193                return -ENOBUFS;
 194
 195        return 0;
 196}
 197
 198/**
 199 * batadv_netlink_tpmeter_notify - send tp_meter result via netlink to client
 200 * @bat_priv: the bat priv with all the soft interface information
 201 * @dst: destination of tp_meter session
 202 * @result: reason for tp meter session stop
 203 * @test_time: total time ot the tp_meter session
 204 * @total_bytes: bytes acked to the receiver
 205 * @cookie: cookie of tp_meter session
 206 *
 207 * Return: 0 on success, < 0 on error
 208 */
 209int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
 210                                  u8 result, u32 test_time, u64 total_bytes,
 211                                  u32 cookie)
 212{
 213        struct sk_buff *msg;
 214        void *hdr;
 215        int ret;
 216
 217        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 218        if (!msg)
 219                return -ENOMEM;
 220
 221        hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
 222                          BATADV_CMD_TP_METER);
 223        if (!hdr) {
 224                ret = -ENOBUFS;
 225                goto err_genlmsg;
 226        }
 227
 228        if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
 229                goto nla_put_failure;
 230
 231        if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time))
 232                goto nla_put_failure;
 233
 234        if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes,
 235                              BATADV_ATTR_PAD))
 236                goto nla_put_failure;
 237
 238        if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result))
 239                goto nla_put_failure;
 240
 241        if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst))
 242                goto nla_put_failure;
 243
 244        genlmsg_end(msg, hdr);
 245
 246        genlmsg_multicast_netns(&batadv_netlink_family,
 247                                dev_net(bat_priv->soft_iface), msg, 0,
 248                                BATADV_NL_MCGRP_TPMETER, GFP_KERNEL);
 249
 250        return 0;
 251
 252nla_put_failure:
 253        genlmsg_cancel(msg, hdr);
 254        ret = -EMSGSIZE;
 255
 256err_genlmsg:
 257        nlmsg_free(msg);
 258        return ret;
 259}
 260
 261/**
 262 * batadv_netlink_tp_meter_start - Start a new tp_meter session
 263 * @skb: received netlink message
 264 * @info: receiver information
 265 *
 266 * Return: 0 on success, < 0 on error
 267 */
 268static int
 269batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
 270{
 271        struct net *net = genl_info_net(info);
 272        struct net_device *soft_iface;
 273        struct batadv_priv *bat_priv;
 274        struct sk_buff *msg = NULL;
 275        u32 test_length;
 276        void *msg_head;
 277        int ifindex;
 278        u32 cookie;
 279        u8 *dst;
 280        int ret;
 281
 282        if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
 283                return -EINVAL;
 284
 285        if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
 286                return -EINVAL;
 287
 288        if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
 289                return -EINVAL;
 290
 291        ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
 292        if (!ifindex)
 293                return -EINVAL;
 294
 295        dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
 296
 297        test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
 298
 299        soft_iface = dev_get_by_index(net, ifindex);
 300        if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
 301                ret = -ENODEV;
 302                goto out;
 303        }
 304
 305        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 306        if (!msg) {
 307                ret = -ENOMEM;
 308                goto out;
 309        }
 310
 311        msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
 312                               &batadv_netlink_family, 0,
 313                               BATADV_CMD_TP_METER);
 314        if (!msg_head) {
 315                ret = -ENOBUFS;
 316                goto out;
 317        }
 318
 319        bat_priv = netdev_priv(soft_iface);
 320        batadv_tp_start(bat_priv, dst, test_length, &cookie);
 321
 322        ret = batadv_netlink_tp_meter_put(msg, cookie);
 323
 324 out:
 325        if (soft_iface)
 326                dev_put(soft_iface);
 327
 328        if (ret) {
 329                if (msg)
 330                        nlmsg_free(msg);
 331                return ret;
 332        }
 333
 334        genlmsg_end(msg, msg_head);
 335        return genlmsg_reply(msg, info);
 336}
 337
 338/**
 339 * batadv_netlink_tp_meter_start - Cancel a running tp_meter session
 340 * @skb: received netlink message
 341 * @info: receiver information
 342 *
 343 * Return: 0 on success, < 0 on error
 344 */
 345static int
 346batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
 347{
 348        struct net *net = genl_info_net(info);
 349        struct net_device *soft_iface;
 350        struct batadv_priv *bat_priv;
 351        int ifindex;
 352        u8 *dst;
 353        int ret = 0;
 354
 355        if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
 356                return -EINVAL;
 357
 358        if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
 359                return -EINVAL;
 360
 361        ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
 362        if (!ifindex)
 363                return -EINVAL;
 364
 365        dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
 366
 367        soft_iface = dev_get_by_index(net, ifindex);
 368        if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
 369                ret = -ENODEV;
 370                goto out;
 371        }
 372
 373        bat_priv = netdev_priv(soft_iface);
 374        batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
 375
 376out:
 377        if (soft_iface)
 378                dev_put(soft_iface);
 379
 380        return ret;
 381}
 382
 383static struct genl_ops batadv_netlink_ops[] = {
 384        {
 385                .cmd = BATADV_CMD_GET_MESH_INFO,
 386                .flags = GENL_ADMIN_PERM,
 387                .policy = batadv_netlink_policy,
 388                .doit = batadv_netlink_get_mesh_info,
 389        },
 390        {
 391                .cmd = BATADV_CMD_TP_METER,
 392                .flags = GENL_ADMIN_PERM,
 393                .policy = batadv_netlink_policy,
 394                .doit = batadv_netlink_tp_meter_start,
 395        },
 396        {
 397                .cmd = BATADV_CMD_TP_METER_CANCEL,
 398                .flags = GENL_ADMIN_PERM,
 399                .policy = batadv_netlink_policy,
 400                .doit = batadv_netlink_tp_meter_cancel,
 401        },
 402};
 403
 404/**
 405 * batadv_netlink_register - register batadv genl netlink family
 406 */
 407void __init batadv_netlink_register(void)
 408{
 409        int ret;
 410
 411        ret = genl_register_family_with_ops_groups(&batadv_netlink_family,
 412                                                   batadv_netlink_ops,
 413                                                   batadv_netlink_mcgrps);
 414        if (ret)
 415                pr_warn("unable to register netlink family");
 416}
 417
 418/**
 419 * batadv_netlink_unregister - unregister batadv genl netlink family
 420 */
 421void batadv_netlink_unregister(void)
 422{
 423        genl_unregister_family(&batadv_netlink_family);
 424}
 425