18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (C) 2009-2020 B.A.T.M.A.N. contributors: 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Marek Lindner 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "gateway_common.h" 88c2ecf20Sopenharmony_ci#include "main.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/atomic.h> 118c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/kstrtox.h> 148c2ecf20Sopenharmony_ci#include <linux/limits.h> 158c2ecf20Sopenharmony_ci#include <linux/math64.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/stddef.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 208c2ecf20Sopenharmony_ci#include <uapi/linux/batman_adv.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "gateway_client.h" 238c2ecf20Sopenharmony_ci#include "log.h" 248c2ecf20Sopenharmony_ci#include "tvlv.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/** 278c2ecf20Sopenharmony_ci * batadv_parse_throughput() - parse supplied string buffer to extract 288c2ecf20Sopenharmony_ci * throughput information 298c2ecf20Sopenharmony_ci * @net_dev: the soft interface net device 308c2ecf20Sopenharmony_ci * @buff: string buffer to parse 318c2ecf20Sopenharmony_ci * @description: text shown when throughput string cannot be parsed 328c2ecf20Sopenharmony_ci * @throughput: pointer holding the returned throughput information 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Return: false on parse error and true otherwise. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cibool batadv_parse_throughput(struct net_device *net_dev, char *buff, 378c2ecf20Sopenharmony_ci const char *description, u32 *throughput) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT; 408c2ecf20Sopenharmony_ci u64 lthroughput; 418c2ecf20Sopenharmony_ci char *tmp_ptr; 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (strlen(buff) > 4) { 458c2ecf20Sopenharmony_ci tmp_ptr = buff + strlen(buff) - 4; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (strncasecmp(tmp_ptr, "mbit", 4) == 0) 488c2ecf20Sopenharmony_ci bw_unit_type = BATADV_BW_UNIT_MBIT; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (strncasecmp(tmp_ptr, "kbit", 4) == 0 || 518c2ecf20Sopenharmony_ci bw_unit_type == BATADV_BW_UNIT_MBIT) 528c2ecf20Sopenharmony_ci *tmp_ptr = '\0'; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = kstrtou64(buff, 10, <hroughput); 568c2ecf20Sopenharmony_ci if (ret) { 578c2ecf20Sopenharmony_ci batadv_err(net_dev, 588c2ecf20Sopenharmony_ci "Invalid throughput speed for %s: %s\n", 598c2ecf20Sopenharmony_ci description, buff); 608c2ecf20Sopenharmony_ci return false; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci switch (bw_unit_type) { 648c2ecf20Sopenharmony_ci case BATADV_BW_UNIT_MBIT: 658c2ecf20Sopenharmony_ci /* prevent overflow */ 668c2ecf20Sopenharmony_ci if (U64_MAX / 10 < lthroughput) { 678c2ecf20Sopenharmony_ci batadv_err(net_dev, 688c2ecf20Sopenharmony_ci "Throughput speed for %s too large: %s\n", 698c2ecf20Sopenharmony_ci description, buff); 708c2ecf20Sopenharmony_ci return false; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci lthroughput *= 10; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case BATADV_BW_UNIT_KBIT: 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci lthroughput = div_u64(lthroughput, 100); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (lthroughput > U32_MAX) { 828c2ecf20Sopenharmony_ci batadv_err(net_dev, 838c2ecf20Sopenharmony_ci "Throughput speed for %s too large: %s\n", 848c2ecf20Sopenharmony_ci description, buff); 858c2ecf20Sopenharmony_ci return false; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci *throughput = lthroughput; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return true; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * batadv_parse_gw_bandwidth() - parse supplied string buffer to extract 958c2ecf20Sopenharmony_ci * download and upload bandwidth information 968c2ecf20Sopenharmony_ci * @net_dev: the soft interface net device 978c2ecf20Sopenharmony_ci * @buff: string buffer to parse 988c2ecf20Sopenharmony_ci * @down: pointer holding the returned download bandwidth information 998c2ecf20Sopenharmony_ci * @up: pointer holding the returned upload bandwidth information 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Return: false on parse error and true otherwise. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, 1048c2ecf20Sopenharmony_ci u32 *down, u32 *up) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci char *slash_ptr; 1078c2ecf20Sopenharmony_ci bool ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci slash_ptr = strchr(buff, '/'); 1108c2ecf20Sopenharmony_ci if (slash_ptr) 1118c2ecf20Sopenharmony_ci *slash_ptr = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = batadv_parse_throughput(net_dev, buff, "download gateway speed", 1148c2ecf20Sopenharmony_ci down); 1158c2ecf20Sopenharmony_ci if (!ret) 1168c2ecf20Sopenharmony_ci return false; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* we also got some upload info */ 1198c2ecf20Sopenharmony_ci if (slash_ptr) { 1208c2ecf20Sopenharmony_ci ret = batadv_parse_throughput(net_dev, slash_ptr + 1, 1218c2ecf20Sopenharmony_ci "upload gateway speed", up); 1228c2ecf20Sopenharmony_ci if (!ret) 1238c2ecf20Sopenharmony_ci return false; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return true; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * batadv_gw_tvlv_container_update() - update the gw tvlv container after 1318c2ecf20Sopenharmony_ci * gateway setting change 1328c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_civoid batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct batadv_tvlv_gateway_data gw; 1378c2ecf20Sopenharmony_ci u32 down, up; 1388c2ecf20Sopenharmony_ci char gw_mode; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci gw_mode = atomic_read(&bat_priv->gw.mode); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci switch (gw_mode) { 1438c2ecf20Sopenharmony_ci case BATADV_GW_MODE_OFF: 1448c2ecf20Sopenharmony_ci case BATADV_GW_MODE_CLIENT: 1458c2ecf20Sopenharmony_ci batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci case BATADV_GW_MODE_SERVER: 1488c2ecf20Sopenharmony_ci down = atomic_read(&bat_priv->gw.bandwidth_down); 1498c2ecf20Sopenharmony_ci up = atomic_read(&bat_priv->gw.bandwidth_up); 1508c2ecf20Sopenharmony_ci gw.bandwidth_down = htonl(down); 1518c2ecf20Sopenharmony_ci gw.bandwidth_up = htonl(up); 1528c2ecf20Sopenharmony_ci batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1, 1538c2ecf20Sopenharmony_ci &gw, sizeof(gw)); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * batadv_gw_bandwidth_set() - Parse and set download/upload gateway bandwidth 1608c2ecf20Sopenharmony_ci * from supplied string buffer 1618c2ecf20Sopenharmony_ci * @net_dev: netdev struct of the soft interface 1628c2ecf20Sopenharmony_ci * @buff: the buffer containing the user data 1638c2ecf20Sopenharmony_ci * @count: number of bytes in the buffer 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Return: 'count' on success or a negative error code in case of failure 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cissize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, 1688c2ecf20Sopenharmony_ci size_t count) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(net_dev); 1718c2ecf20Sopenharmony_ci u32 down_curr; 1728c2ecf20Sopenharmony_ci u32 up_curr; 1738c2ecf20Sopenharmony_ci u32 down_new = 0; 1748c2ecf20Sopenharmony_ci u32 up_new = 0; 1758c2ecf20Sopenharmony_ci bool ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down); 1788c2ecf20Sopenharmony_ci up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new); 1818c2ecf20Sopenharmony_ci if (!ret) 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!down_new) 1858c2ecf20Sopenharmony_ci down_new = 1; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!up_new) 1888c2ecf20Sopenharmony_ci up_new = down_new / 5; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!up_new) 1918c2ecf20Sopenharmony_ci up_new = 1; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (down_curr == down_new && up_curr == up_new) 1948c2ecf20Sopenharmony_ci return count; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci batadv_gw_reselect(bat_priv); 1978c2ecf20Sopenharmony_ci batadv_info(net_dev, 1988c2ecf20Sopenharmony_ci "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n", 1998c2ecf20Sopenharmony_ci down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10, 2008c2ecf20Sopenharmony_ci down_new / 10, down_new % 10, up_new / 10, up_new % 10); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci atomic_set(&bat_priv->gw.bandwidth_down, down_new); 2038c2ecf20Sopenharmony_ci atomic_set(&bat_priv->gw.bandwidth_up, up_new); 2048c2ecf20Sopenharmony_ci batadv_gw_tvlv_container_update(bat_priv); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return count; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * batadv_gw_tvlv_ogm_handler_v1() - process incoming gateway tvlv container 2118c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 2128c2ecf20Sopenharmony_ci * @orig: the orig_node of the ogm 2138c2ecf20Sopenharmony_ci * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) 2148c2ecf20Sopenharmony_ci * @tvlv_value: tvlv buffer containing the gateway data 2158c2ecf20Sopenharmony_ci * @tvlv_value_len: tvlv buffer length 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_cistatic void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, 2188c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 2198c2ecf20Sopenharmony_ci u8 flags, 2208c2ecf20Sopenharmony_ci void *tvlv_value, u16 tvlv_value_len) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct batadv_tvlv_gateway_data gateway, *gateway_ptr; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* only fetch the tvlv value if the handler wasn't called via the 2258c2ecf20Sopenharmony_ci * CIFNOTFND flag and if there is data to fetch 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND || 2288c2ecf20Sopenharmony_ci tvlv_value_len < sizeof(gateway)) { 2298c2ecf20Sopenharmony_ci gateway.bandwidth_down = 0; 2308c2ecf20Sopenharmony_ci gateway.bandwidth_up = 0; 2318c2ecf20Sopenharmony_ci } else { 2328c2ecf20Sopenharmony_ci gateway_ptr = tvlv_value; 2338c2ecf20Sopenharmony_ci gateway.bandwidth_down = gateway_ptr->bandwidth_down; 2348c2ecf20Sopenharmony_ci gateway.bandwidth_up = gateway_ptr->bandwidth_up; 2358c2ecf20Sopenharmony_ci if (gateway.bandwidth_down == 0 || 2368c2ecf20Sopenharmony_ci gateway.bandwidth_up == 0) { 2378c2ecf20Sopenharmony_ci gateway.bandwidth_down = 0; 2388c2ecf20Sopenharmony_ci gateway.bandwidth_up = 0; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci batadv_gw_node_update(bat_priv, orig, &gateway); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* restart gateway selection */ 2458c2ecf20Sopenharmony_ci if (gateway.bandwidth_down != 0 && 2468c2ecf20Sopenharmony_ci atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT) 2478c2ecf20Sopenharmony_ci batadv_gw_check_election(bat_priv, orig); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * batadv_gw_init() - initialise the gateway handling internals 2528c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_civoid batadv_gw_init(struct batadv_priv *bat_priv) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci if (bat_priv->algo_ops->gw.init_sel_class) 2578c2ecf20Sopenharmony_ci bat_priv->algo_ops->gw.init_sel_class(bat_priv); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci atomic_set(&bat_priv->gw.sel_class, 1); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, 2628c2ecf20Sopenharmony_ci NULL, BATADV_TVLV_GW, 1, 2638c2ecf20Sopenharmony_ci BATADV_TVLV_HANDLER_OGM_CIFNOTFND); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * batadv_gw_free() - free the gateway handling internals 2688c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_civoid batadv_gw_free(struct batadv_priv *bat_priv) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); 2738c2ecf20Sopenharmony_ci batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1); 2748c2ecf20Sopenharmony_ci} 275