linux/net/batman-adv/gateway_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (C) B.A.T.M.A.N. contributors:
   3 *
   4 * Marek Lindner
   5 */
   6
   7#include "gateway_common.h"
   8#include "main.h"
   9
  10#include <linux/atomic.h>
  11#include <linux/byteorder/generic.h>
  12#include <linux/errno.h>
  13#include <linux/kernel.h>
  14#include <linux/limits.h>
  15#include <linux/math64.h>
  16#include <linux/netdevice.h>
  17#include <linux/stddef.h>
  18#include <linux/string.h>
  19#include <uapi/linux/batadv_packet.h>
  20#include <uapi/linux/batman_adv.h>
  21
  22#include "gateway_client.h"
  23#include "log.h"
  24#include "tvlv.h"
  25
  26/**
  27 * batadv_parse_throughput() - parse supplied string buffer to extract
  28 *  throughput information
  29 * @net_dev: the soft interface net device
  30 * @buff: string buffer to parse
  31 * @description: text shown when throughput string cannot be parsed
  32 * @throughput: pointer holding the returned throughput information
  33 *
  34 * Return: false on parse error and true otherwise.
  35 */
  36bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
  37                             const char *description, u32 *throughput)
  38{
  39        enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
  40        u64 lthroughput;
  41        char *tmp_ptr;
  42        int ret;
  43
  44        if (strlen(buff) > 4) {
  45                tmp_ptr = buff + strlen(buff) - 4;
  46
  47                if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
  48                        bw_unit_type = BATADV_BW_UNIT_MBIT;
  49
  50                if (strncasecmp(tmp_ptr, "kbit", 4) == 0 ||
  51                    bw_unit_type == BATADV_BW_UNIT_MBIT)
  52                        *tmp_ptr = '\0';
  53        }
  54
  55        ret = kstrtou64(buff, 10, &lthroughput);
  56        if (ret) {
  57                batadv_err(net_dev,
  58                           "Invalid throughput speed for %s: %s\n",
  59                           description, buff);
  60                return false;
  61        }
  62
  63        switch (bw_unit_type) {
  64        case BATADV_BW_UNIT_MBIT:
  65                /* prevent overflow */
  66                if (U64_MAX / 10 < lthroughput) {
  67                        batadv_err(net_dev,
  68                                   "Throughput speed for %s too large: %s\n",
  69                                   description, buff);
  70                        return false;
  71                }
  72
  73                lthroughput *= 10;
  74                break;
  75        case BATADV_BW_UNIT_KBIT:
  76        default:
  77                lthroughput = div_u64(lthroughput, 100);
  78                break;
  79        }
  80
  81        if (lthroughput > U32_MAX) {
  82                batadv_err(net_dev,
  83                           "Throughput speed for %s too large: %s\n",
  84                           description, buff);
  85                return false;
  86        }
  87
  88        *throughput = lthroughput;
  89
  90        return true;
  91}
  92
  93/**
  94 * batadv_parse_gw_bandwidth() - parse supplied string buffer to extract
  95 *  download and upload bandwidth information
  96 * @net_dev: the soft interface net device
  97 * @buff: string buffer to parse
  98 * @down: pointer holding the returned download bandwidth information
  99 * @up: pointer holding the returned upload bandwidth information
 100 *
 101 * Return: false on parse error and true otherwise.
 102 */
 103static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
 104                                      u32 *down, u32 *up)
 105{
 106        char *slash_ptr;
 107        bool ret;
 108
 109        slash_ptr = strchr(buff, '/');
 110        if (slash_ptr)
 111                *slash_ptr = 0;
 112
 113        ret = batadv_parse_throughput(net_dev, buff, "download gateway speed",
 114                                      down);
 115        if (!ret)
 116                return false;
 117
 118        /* we also got some upload info */
 119        if (slash_ptr) {
 120                ret = batadv_parse_throughput(net_dev, slash_ptr + 1,
 121                                              "upload gateway speed", up);
 122                if (!ret)
 123                        return false;
 124        }
 125
 126        return true;
 127}
 128
 129/**
 130 * batadv_gw_tvlv_container_update() - update the gw tvlv container after
 131 *  gateway setting change
 132 * @bat_priv: the bat priv with all the soft interface information
 133 */
 134void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
 135{
 136        struct batadv_tvlv_gateway_data gw;
 137        u32 down, up;
 138        char gw_mode;
 139
 140        gw_mode = atomic_read(&bat_priv->gw.mode);
 141
 142        switch (gw_mode) {
 143        case BATADV_GW_MODE_OFF:
 144        case BATADV_GW_MODE_CLIENT:
 145                batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
 146                break;
 147        case BATADV_GW_MODE_SERVER:
 148                down = atomic_read(&bat_priv->gw.bandwidth_down);
 149                up = atomic_read(&bat_priv->gw.bandwidth_up);
 150                gw.bandwidth_down = htonl(down);
 151                gw.bandwidth_up = htonl(up);
 152                batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1,
 153                                               &gw, sizeof(gw));
 154                break;
 155        }
 156}
 157
 158/**
 159 * batadv_gw_bandwidth_set() - Parse and set download/upload gateway bandwidth
 160 *  from supplied string buffer
 161 * @net_dev: netdev struct of the soft interface
 162 * @buff: the buffer containing the user data
 163 * @count: number of bytes in the buffer
 164 *
 165 * Return: 'count' on success or a negative error code in case of failure
 166 */
 167ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
 168                                size_t count)
 169{
 170        struct batadv_priv *bat_priv = netdev_priv(net_dev);
 171        u32 down_curr;
 172        u32 up_curr;
 173        u32 down_new = 0;
 174        u32 up_new = 0;
 175        bool ret;
 176
 177        down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down);
 178        up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up);
 179
 180        ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new);
 181        if (!ret)
 182                return -EINVAL;
 183
 184        if (!down_new)
 185                down_new = 1;
 186
 187        if (!up_new)
 188                up_new = down_new / 5;
 189
 190        if (!up_new)
 191                up_new = 1;
 192
 193        if (down_curr == down_new && up_curr == up_new)
 194                return count;
 195
 196        batadv_gw_reselect(bat_priv);
 197        batadv_info(net_dev,
 198                    "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n",
 199                    down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10,
 200                    down_new / 10, down_new % 10, up_new / 10, up_new % 10);
 201
 202        atomic_set(&bat_priv->gw.bandwidth_down, down_new);
 203        atomic_set(&bat_priv->gw.bandwidth_up, up_new);
 204        batadv_gw_tvlv_container_update(bat_priv);
 205
 206        return count;
 207}
 208
 209/**
 210 * batadv_gw_tvlv_ogm_handler_v1() - process incoming gateway tvlv container
 211 * @bat_priv: the bat priv with all the soft interface information
 212 * @orig: the orig_node of the ogm
 213 * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
 214 * @tvlv_value: tvlv buffer containing the gateway data
 215 * @tvlv_value_len: tvlv buffer length
 216 */
 217static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
 218                                          struct batadv_orig_node *orig,
 219                                          u8 flags,
 220                                          void *tvlv_value, u16 tvlv_value_len)
 221{
 222        struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
 223
 224        /* only fetch the tvlv value if the handler wasn't called via the
 225         * CIFNOTFND flag and if there is data to fetch
 226         */
 227        if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND ||
 228            tvlv_value_len < sizeof(gateway)) {
 229                gateway.bandwidth_down = 0;
 230                gateway.bandwidth_up = 0;
 231        } else {
 232                gateway_ptr = tvlv_value;
 233                gateway.bandwidth_down = gateway_ptr->bandwidth_down;
 234                gateway.bandwidth_up = gateway_ptr->bandwidth_up;
 235                if (gateway.bandwidth_down == 0 ||
 236                    gateway.bandwidth_up == 0) {
 237                        gateway.bandwidth_down = 0;
 238                        gateway.bandwidth_up = 0;
 239                }
 240        }
 241
 242        batadv_gw_node_update(bat_priv, orig, &gateway);
 243
 244        /* restart gateway selection */
 245        if (gateway.bandwidth_down != 0 &&
 246            atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT)
 247                batadv_gw_check_election(bat_priv, orig);
 248}
 249
 250/**
 251 * batadv_gw_init() - initialise the gateway handling internals
 252 * @bat_priv: the bat priv with all the soft interface information
 253 */
 254void batadv_gw_init(struct batadv_priv *bat_priv)
 255{
 256        if (bat_priv->algo_ops->gw.init_sel_class)
 257                bat_priv->algo_ops->gw.init_sel_class(bat_priv);
 258        else
 259                atomic_set(&bat_priv->gw.sel_class, 1);
 260
 261        batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
 262                                     NULL, BATADV_TVLV_GW, 1,
 263                                     BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
 264}
 265
 266/**
 267 * batadv_gw_free() - free the gateway handling internals
 268 * @bat_priv: the bat priv with all the soft interface information
 269 */
 270void batadv_gw_free(struct batadv_priv *bat_priv)
 271{
 272        batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
 273        batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
 274}
 275