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