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