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