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/byteorder/generic.h> 23 #include <linux/errno.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 "log.h" 32 #include "packet.h" 33 #include "tvlv.h" 34 35 /** 36 * batadv_parse_throughput - parse supplied string buffer to extract throughput 37 * information 38 * @net_dev: the soft interface net device 39 * @buff: string buffer to parse 40 * @description: text shown when throughput string cannot be parsed 41 * @throughput: pointer holding the returned throughput information 42 * 43 * Return: false on parse error and true otherwise. 44 */ 45 bool batadv_parse_throughput(struct net_device *net_dev, char *buff, 46 const char *description, u32 *throughput) 47 { 48 enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT; 49 u64 lthroughput; 50 char *tmp_ptr; 51 int ret; 52 53 if (strlen(buff) > 4) { 54 tmp_ptr = buff + strlen(buff) - 4; 55 56 if (strncasecmp(tmp_ptr, "mbit", 4) == 0) 57 bw_unit_type = BATADV_BW_UNIT_MBIT; 58 59 if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) || 60 (bw_unit_type == BATADV_BW_UNIT_MBIT)) 61 *tmp_ptr = '\0'; 62 } 63 64 ret = kstrtou64(buff, 10, <hroughput); 65 if (ret) { 66 batadv_err(net_dev, 67 "Invalid throughput speed for %s: %s\n", 68 description, buff); 69 return false; 70 } 71 72 switch (bw_unit_type) { 73 case BATADV_BW_UNIT_MBIT: 74 /* prevent overflow */ 75 if (U64_MAX / 10 < lthroughput) { 76 batadv_err(net_dev, 77 "Throughput speed for %s too large: %s\n", 78 description, buff); 79 return false; 80 } 81 82 lthroughput *= 10; 83 break; 84 case BATADV_BW_UNIT_KBIT: 85 default: 86 lthroughput = div_u64(lthroughput, 100); 87 break; 88 } 89 90 if (lthroughput > U32_MAX) { 91 batadv_err(net_dev, 92 "Throughput speed for %s too large: %s\n", 93 description, buff); 94 return false; 95 } 96 97 *throughput = lthroughput; 98 99 return true; 100 } 101 102 /** 103 * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download 104 * and upload bandwidth information 105 * @net_dev: the soft interface net device 106 * @buff: string buffer to parse 107 * @down: pointer holding the returned download bandwidth information 108 * @up: pointer holding the returned upload bandwidth information 109 * 110 * Return: false on parse error and true otherwise. 111 */ 112 static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, 113 u32 *down, u32 *up) 114 { 115 char *slash_ptr; 116 bool ret; 117 118 slash_ptr = strchr(buff, '/'); 119 if (slash_ptr) 120 *slash_ptr = 0; 121 122 ret = batadv_parse_throughput(net_dev, buff, "download gateway speed", 123 down); 124 if (!ret) 125 return false; 126 127 /* we also got some upload info */ 128 if (slash_ptr) { 129 ret = batadv_parse_throughput(net_dev, slash_ptr + 1, 130 "upload gateway speed", up); 131 if (!ret) 132 return false; 133 } 134 135 return true; 136 } 137 138 /** 139 * batadv_gw_tvlv_container_update - update the gw tvlv container after gateway 140 * setting change 141 * @bat_priv: the bat priv with all the soft interface information 142 */ 143 void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) 144 { 145 struct batadv_tvlv_gateway_data gw; 146 u32 down, up; 147 char gw_mode; 148 149 gw_mode = atomic_read(&bat_priv->gw.mode); 150 151 switch (gw_mode) { 152 case BATADV_GW_MODE_OFF: 153 case BATADV_GW_MODE_CLIENT: 154 batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); 155 break; 156 case BATADV_GW_MODE_SERVER: 157 down = atomic_read(&bat_priv->gw.bandwidth_down); 158 up = atomic_read(&bat_priv->gw.bandwidth_up); 159 gw.bandwidth_down = htonl(down); 160 gw.bandwidth_up = htonl(up); 161 batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1, 162 &gw, sizeof(gw)); 163 break; 164 } 165 } 166 167 ssize_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 */ 217 static 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 if fast or late switching was enabled */ 245 if ((gateway.bandwidth_down != 0) && 246 (atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT) && 247 (atomic_read(&bat_priv->gw.sel_class) > 2)) 248 batadv_gw_check_election(bat_priv, orig); 249 } 250 251 /** 252 * batadv_gw_init - initialise the gateway handling internals 253 * @bat_priv: the bat priv with all the soft interface information 254 */ 255 void batadv_gw_init(struct batadv_priv *bat_priv) 256 { 257 batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, 258 NULL, BATADV_TVLV_GW, 1, 259 BATADV_TVLV_HANDLER_OGM_CIFNOTFND); 260 } 261 262 /** 263 * batadv_gw_free - free the gateway handling internals 264 * @bat_priv: the bat priv with all the soft interface information 265 */ 266 void batadv_gw_free(struct batadv_priv *bat_priv) 267 { 268 batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); 269 batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1); 270 } 271