Commit | Line | Data |
---|---|---|
7db7d9f3 | 1 | // SPDX-License-Identifier: GPL-2.0 |
7a79d717 | 2 | /* Copyright (C) 2010-2019 B.A.T.M.A.N. contributors: |
c6c8fea2 SE |
3 | * |
4 | * Marek Lindner | |
c6c8fea2 SE |
5 | */ |
6 | ||
b706b13b | 7 | #include "sysfs.h" |
1e2c2a4f SE |
8 | #include "main.h" |
9 | ||
1392f553 | 10 | #include <asm/current.h> |
1e2c2a4f SE |
11 | #include <linux/atomic.h> |
12 | #include <linux/compiler.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/errno.h> | |
b92b94ac | 15 | #include <linux/gfp.h> |
1e2c2a4f SE |
16 | #include <linux/if.h> |
17 | #include <linux/if_vlan.h> | |
18 | #include <linux/kernel.h> | |
8cfba951 | 19 | #include <linux/kobject.h> |
fcafa5e7 | 20 | #include <linux/kref.h> |
e1928752 | 21 | #include <linux/limits.h> |
1e2c2a4f SE |
22 | #include <linux/netdevice.h> |
23 | #include <linux/printk.h> | |
24 | #include <linux/rculist.h> | |
25 | #include <linux/rcupdate.h> | |
26 | #include <linux/rtnetlink.h> | |
1392f553 | 27 | #include <linux/sched.h> |
1e2c2a4f | 28 | #include <linux/slab.h> |
1e2c2a4f SE |
29 | #include <linux/stddef.h> |
30 | #include <linux/string.h> | |
31 | #include <linux/stringify.h> | |
77d69d8c | 32 | #include <linux/workqueue.h> |
fec149f5 | 33 | #include <uapi/linux/batadv_packet.h> |
e2d0d35b | 34 | #include <uapi/linux/batman_adv.h> |
1e2c2a4f | 35 | |
fcafa5e7 | 36 | #include "bridge_loop_avoidance.h" |
33af49ad | 37 | #include "distributed-arp-table.h" |
1e2c2a4f SE |
38 | #include "gateway_client.h" |
39 | #include "gateway_common.h" | |
c6c8fea2 | 40 | #include "hard-interface.h" |
ba412080 | 41 | #include "log.h" |
7e6f461e | 42 | #include "netlink.h" |
1e2c2a4f | 43 | #include "network-coding.h" |
90f4435d | 44 | #include "soft-interface.h" |
c6c8fea2 | 45 | |
1392f553 SE |
46 | /** |
47 | * batadv_sysfs_deprecated() - Log use of deprecated batadv sysfs access | |
48 | * @attr: attribute which was accessed | |
49 | */ | |
50 | static void batadv_sysfs_deprecated(struct attribute *attr) | |
51 | { | |
52 | pr_warn_ratelimited(DEPRECATED "%s (pid %d) Use of sysfs file \"%s\".\nUse batadv genl family instead", | |
53 | current->comm, task_pid_nr(current), attr->name); | |
54 | } | |
55 | ||
0ff9b86f | 56 | static struct net_device *batadv_kobj_to_netdev(struct kobject *obj) |
3d222bba SE |
57 | { |
58 | struct device *dev = container_of(obj->parent, struct device, kobj); | |
f138694b | 59 | |
3d222bba SE |
60 | return to_net_dev(dev); |
61 | } | |
62 | ||
56303d34 | 63 | static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj) |
3d222bba | 64 | { |
0ff9b86f | 65 | struct net_device *net_dev = batadv_kobj_to_netdev(obj); |
f138694b | 66 | |
3d222bba SE |
67 | return netdev_priv(net_dev); |
68 | } | |
c6c8fea2 | 69 | |
90f4435d | 70 | /** |
7e9a8c2c | 71 | * batadv_vlan_kobj_to_batpriv() - convert a vlan kobj in the associated batpriv |
90f4435d AQ |
72 | * @obj: kobject to covert |
73 | * | |
62fe710f | 74 | * Return: the associated batadv_priv struct. |
90f4435d AQ |
75 | */ |
76 | static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj) | |
77 | { | |
78 | /* VLAN specific attributes are located in the root sysfs folder if they | |
79 | * refer to the untagged VLAN.. | |
80 | */ | |
81 | if (!strcmp(BATADV_SYSFS_IF_MESH_SUBDIR, obj->name)) | |
82 | return batadv_kobj_to_batpriv(obj); | |
83 | ||
84 | /* ..while the attributes for the tagged vlans are located in | |
85 | * the in the corresponding "vlan%VID" subfolder | |
86 | */ | |
87 | return batadv_kobj_to_batpriv(obj->parent); | |
88 | } | |
89 | ||
90 | /** | |
7e9a8c2c | 91 | * batadv_kobj_to_vlan() - convert a kobj in the associated softif_vlan struct |
7afcbbef | 92 | * @bat_priv: the bat priv with all the soft interface information |
90f4435d AQ |
93 | * @obj: kobject to covert |
94 | * | |
62fe710f | 95 | * Return: the associated softif_vlan struct if found, NULL otherwise. |
90f4435d AQ |
96 | */ |
97 | static struct batadv_softif_vlan * | |
98 | batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj) | |
99 | { | |
100 | struct batadv_softif_vlan *vlan_tmp, *vlan = NULL; | |
101 | ||
102 | rcu_read_lock(); | |
103 | hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) { | |
104 | if (vlan_tmp->kobj != obj) | |
105 | continue; | |
106 | ||
6be4d30c | 107 | if (!kref_get_unless_zero(&vlan_tmp->refcount)) |
90f4435d AQ |
108 | continue; |
109 | ||
110 | vlan = vlan_tmp; | |
111 | break; | |
112 | } | |
113 | rcu_read_unlock(); | |
114 | ||
115 | return vlan; | |
116 | } | |
117 | ||
90f4435d AQ |
118 | /* Use this, if you have customized show and store functions for vlan attrs */ |
119 | #define BATADV_ATTR_VLAN(_name, _mode, _show, _store) \ | |
120 | struct batadv_attribute batadv_attr_vlan_##_name = { \ | |
121 | .attr = {.name = __stringify(_name), \ | |
122 | .mode = _mode }, \ | |
123 | .show = _show, \ | |
124 | .store = _store, \ | |
2b64df20 | 125 | } |
90f4435d | 126 | |
c6c8fea2 | 127 | /* Use this, if you have customized show and store functions */ |
347c80f0 | 128 | #define BATADV_ATTR(_name, _mode, _show, _store) \ |
b4d66b87 | 129 | struct batadv_attribute batadv_attr_##_name = { \ |
347c80f0 SE |
130 | .attr = {.name = __stringify(_name), \ |
131 | .mode = _mode }, \ | |
132 | .show = _show, \ | |
133 | .store = _store, \ | |
2b64df20 | 134 | } |
c6c8fea2 | 135 | |
347c80f0 | 136 | #define BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func) \ |
0ff9b86f SE |
137 | ssize_t batadv_store_##_name(struct kobject *kobj, \ |
138 | struct attribute *attr, char *buff, \ | |
139 | size_t count) \ | |
c6c8fea2 | 140 | { \ |
0ff9b86f | 141 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ |
56303d34 | 142 | struct batadv_priv *bat_priv = netdev_priv(net_dev); \ |
7e6f461e SE |
143 | ssize_t length; \ |
144 | \ | |
1392f553 | 145 | batadv_sysfs_deprecated(attr); \ |
7e6f461e SE |
146 | length = __batadv_store_bool_attr(buff, count, _post_func, attr,\ |
147 | &bat_priv->_name, net_dev); \ | |
f138694b | 148 | \ |
7e6f461e SE |
149 | batadv_netlink_notify_mesh(bat_priv); \ |
150 | \ | |
151 | return length; \ | |
c6c8fea2 SE |
152 | } |
153 | ||
347c80f0 | 154 | #define BATADV_ATTR_SIF_SHOW_BOOL(_name) \ |
0ff9b86f SE |
155 | ssize_t batadv_show_##_name(struct kobject *kobj, \ |
156 | struct attribute *attr, char *buff) \ | |
c6c8fea2 | 157 | { \ |
56303d34 | 158 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \ |
f138694b | 159 | \ |
1392f553 | 160 | batadv_sysfs_deprecated(attr); \ |
c6c8fea2 SE |
161 | return sprintf(buff, "%s\n", \ |
162 | atomic_read(&bat_priv->_name) == 0 ? \ | |
163 | "disabled" : "enabled"); \ | |
164 | } \ | |
165 | ||
f245c38b | 166 | /* Use this, if you are going to turn a [name] in the soft-interface |
9cfc7bd6 SE |
167 | * (bat_priv) on or off |
168 | */ | |
347c80f0 SE |
169 | #define BATADV_ATTR_SIF_BOOL(_name, _mode, _post_func) \ |
170 | static BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func) \ | |
171 | static BATADV_ATTR_SIF_SHOW_BOOL(_name) \ | |
172 | static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ | |
173 | batadv_store_##_name) | |
c6c8fea2 | 174 | |
6f70eb75 | 175 | #define BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func) \ |
0ff9b86f SE |
176 | ssize_t batadv_store_##_name(struct kobject *kobj, \ |
177 | struct attribute *attr, char *buff, \ | |
178 | size_t count) \ | |
c6c8fea2 | 179 | { \ |
0ff9b86f | 180 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ |
56303d34 | 181 | struct batadv_priv *bat_priv = netdev_priv(net_dev); \ |
7e6f461e | 182 | ssize_t length; \ |
f138694b | 183 | \ |
1392f553 | 184 | batadv_sysfs_deprecated(attr); \ |
7e6f461e SE |
185 | length = __batadv_store_uint_attr(buff, count, _min, _max, \ |
186 | _post_func, attr, \ | |
187 | &bat_priv->_var, net_dev, \ | |
188 | NULL); \ | |
189 | \ | |
190 | batadv_netlink_notify_mesh(bat_priv); \ | |
191 | \ | |
192 | return length; \ | |
c6c8fea2 SE |
193 | } |
194 | ||
6f70eb75 | 195 | #define BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \ |
0ff9b86f SE |
196 | ssize_t batadv_show_##_name(struct kobject *kobj, \ |
197 | struct attribute *attr, char *buff) \ | |
c6c8fea2 | 198 | { \ |
56303d34 | 199 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \ |
f138694b | 200 | \ |
1392f553 | 201 | batadv_sysfs_deprecated(attr); \ |
6f70eb75 | 202 | return sprintf(buff, "%i\n", atomic_read(&bat_priv->_var)); \ |
c6c8fea2 SE |
203 | } \ |
204 | ||
f245c38b | 205 | /* Use this, if you are going to set [name] in the soft-interface |
9cfc7bd6 SE |
206 | * (bat_priv) to an unsigned integer value |
207 | */ | |
6f70eb75 AQ |
208 | #define BATADV_ATTR_SIF_UINT(_name, _var, _mode, _min, _max, _post_func)\ |
209 | static BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func)\ | |
210 | static BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \ | |
347c80f0 SE |
211 | static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ |
212 | batadv_store_##_name) | |
c6c8fea2 | 213 | |
90f4435d AQ |
214 | #define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \ |
215 | ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \ | |
216 | struct attribute *attr, char *buff, \ | |
217 | size_t count) \ | |
218 | { \ | |
219 | struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\ | |
220 | struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \ | |
221 | kobj); \ | |
222 | size_t res = __batadv_store_bool_attr(buff, count, _post_func, \ | |
223 | attr, &vlan->_name, \ | |
224 | bat_priv->soft_iface); \ | |
f138694b | 225 | \ |
1392f553 | 226 | batadv_sysfs_deprecated(attr); \ |
7e6f461e SE |
227 | if (vlan->vid) \ |
228 | batadv_netlink_notify_vlan(bat_priv, vlan); \ | |
229 | else \ | |
230 | batadv_netlink_notify_mesh(bat_priv); \ | |
231 | \ | |
9c3bf081 | 232 | batadv_softif_vlan_put(vlan); \ |
90f4435d AQ |
233 | return res; \ |
234 | } | |
235 | ||
236 | #define BATADV_ATTR_VLAN_SHOW_BOOL(_name) \ | |
237 | ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \ | |
238 | struct attribute *attr, char *buff) \ | |
239 | { \ | |
240 | struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\ | |
241 | struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \ | |
242 | kobj); \ | |
243 | size_t res = sprintf(buff, "%s\n", \ | |
244 | atomic_read(&vlan->_name) == 0 ? \ | |
245 | "disabled" : "enabled"); \ | |
f138694b | 246 | \ |
1392f553 | 247 | batadv_sysfs_deprecated(attr); \ |
9c3bf081 | 248 | batadv_softif_vlan_put(vlan); \ |
90f4435d AQ |
249 | return res; \ |
250 | } | |
251 | ||
252 | /* Use this, if you are going to turn a [name] in the vlan struct on or off */ | |
253 | #define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func) \ | |
254 | static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \ | |
255 | static BATADV_ATTR_VLAN_SHOW_BOOL(_name) \ | |
256 | static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \ | |
257 | batadv_store_vlan_##_name) | |
0744ff8f LL |
258 | |
259 | #define BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, _max, _post_func) \ | |
260 | ssize_t batadv_store_##_name(struct kobject *kobj, \ | |
261 | struct attribute *attr, char *buff, \ | |
262 | size_t count) \ | |
263 | { \ | |
264 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ | |
265 | struct batadv_hard_iface *hard_iface; \ | |
7e6f461e | 266 | struct batadv_priv *bat_priv; \ |
0744ff8f LL |
267 | ssize_t length; \ |
268 | \ | |
1392f553 | 269 | batadv_sysfs_deprecated(attr); \ |
0744ff8f LL |
270 | hard_iface = batadv_hardif_get_by_netdev(net_dev); \ |
271 | if (!hard_iface) \ | |
272 | return 0; \ | |
273 | \ | |
274 | length = __batadv_store_uint_attr(buff, count, _min, _max, \ | |
275 | _post_func, attr, \ | |
a25bab9d SE |
276 | &hard_iface->_var, \ |
277 | hard_iface->soft_iface, \ | |
278 | net_dev); \ | |
0744ff8f | 279 | \ |
7e6f461e SE |
280 | if (hard_iface->soft_iface) { \ |
281 | bat_priv = netdev_priv(hard_iface->soft_iface); \ | |
282 | batadv_netlink_notify_hardif(bat_priv, hard_iface); \ | |
283 | } \ | |
284 | \ | |
0744ff8f LL |
285 | batadv_hardif_put(hard_iface); \ |
286 | return length; \ | |
287 | } | |
288 | ||
289 | #define BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ | |
290 | ssize_t batadv_show_##_name(struct kobject *kobj, \ | |
291 | struct attribute *attr, char *buff) \ | |
292 | { \ | |
293 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ | |
294 | struct batadv_hard_iface *hard_iface; \ | |
295 | ssize_t length; \ | |
296 | \ | |
1392f553 | 297 | batadv_sysfs_deprecated(attr); \ |
0744ff8f LL |
298 | hard_iface = batadv_hardif_get_by_netdev(net_dev); \ |
299 | if (!hard_iface) \ | |
300 | return 0; \ | |
301 | \ | |
302 | length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_var)); \ | |
303 | \ | |
304 | batadv_hardif_put(hard_iface); \ | |
305 | return length; \ | |
306 | } | |
307 | ||
308 | /* Use this, if you are going to set [name] in hard_iface to an | |
309 | * unsigned integer value | |
310 | */ | |
311 | #define BATADV_ATTR_HIF_UINT(_name, _var, _mode, _min, _max, _post_func)\ | |
312 | static BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, \ | |
313 | _max, _post_func) \ | |
314 | static BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ | |
315 | static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ | |
316 | batadv_store_##_name) | |
c6c8fea2 | 317 | |
0ff9b86f SE |
318 | static int batadv_store_bool_attr(char *buff, size_t count, |
319 | struct net_device *net_dev, | |
9e728e84 SW |
320 | const char *attr_name, atomic_t *attr, |
321 | bool *changed) | |
c6c8fea2 SE |
322 | { |
323 | int enabled = -1; | |
324 | ||
9e728e84 SW |
325 | *changed = false; |
326 | ||
c6c8fea2 SE |
327 | if (buff[count - 1] == '\n') |
328 | buff[count - 1] = '\0'; | |
329 | ||
330 | if ((strncmp(buff, "1", 2) == 0) || | |
331 | (strncmp(buff, "enable", 7) == 0) || | |
332 | (strncmp(buff, "enabled", 8) == 0)) | |
333 | enabled = 1; | |
334 | ||
335 | if ((strncmp(buff, "0", 2) == 0) || | |
336 | (strncmp(buff, "disable", 8) == 0) || | |
337 | (strncmp(buff, "disabled", 9) == 0)) | |
338 | enabled = 0; | |
339 | ||
340 | if (enabled < 0) { | |
3e34819e SE |
341 | batadv_info(net_dev, "%s: Invalid parameter received: %s\n", |
342 | attr_name, buff); | |
c6c8fea2 SE |
343 | return -EINVAL; |
344 | } | |
345 | ||
346 | if (atomic_read(attr) == enabled) | |
347 | return count; | |
348 | ||
3e34819e SE |
349 | batadv_info(net_dev, "%s: Changing from: %s to: %s\n", attr_name, |
350 | atomic_read(attr) == 1 ? "enabled" : "disabled", | |
351 | enabled == 1 ? "enabled" : "disabled"); | |
c6c8fea2 | 352 | |
9e728e84 SW |
353 | *changed = true; |
354 | ||
95c96174 | 355 | atomic_set(attr, (unsigned int)enabled); |
c6c8fea2 SE |
356 | return count; |
357 | } | |
358 | ||
0ff9b86f SE |
359 | static inline ssize_t |
360 | __batadv_store_bool_attr(char *buff, size_t count, | |
361 | void (*post_func)(struct net_device *), | |
362 | struct attribute *attr, | |
363 | atomic_t *attr_store, struct net_device *net_dev) | |
c6c8fea2 | 364 | { |
9e728e84 | 365 | bool changed; |
c6c8fea2 SE |
366 | int ret; |
367 | ||
0ff9b86f | 368 | ret = batadv_store_bool_attr(buff, count, net_dev, attr->name, |
9e728e84 SW |
369 | attr_store, &changed); |
370 | if (post_func && changed) | |
c6c8fea2 SE |
371 | post_func(net_dev); |
372 | ||
373 | return ret; | |
374 | } | |
375 | ||
0ff9b86f SE |
376 | static int batadv_store_uint_attr(const char *buff, size_t count, |
377 | struct net_device *net_dev, | |
a25bab9d | 378 | struct net_device *slave_dev, |
0ff9b86f SE |
379 | const char *attr_name, |
380 | unsigned int min, unsigned int max, | |
381 | atomic_t *attr) | |
c6c8fea2 | 382 | { |
a25bab9d | 383 | char ifname[IFNAMSIZ + 3] = ""; |
c6c8fea2 SE |
384 | unsigned long uint_val; |
385 | int ret; | |
386 | ||
25a92b13 | 387 | ret = kstrtoul(buff, 10, &uint_val); |
c6c8fea2 | 388 | if (ret) { |
3e34819e SE |
389 | batadv_info(net_dev, "%s: Invalid parameter received: %s\n", |
390 | attr_name, buff); | |
c6c8fea2 SE |
391 | return -EINVAL; |
392 | } | |
393 | ||
394 | if (uint_val < min) { | |
3e34819e SE |
395 | batadv_info(net_dev, "%s: Value is too small: %lu min: %u\n", |
396 | attr_name, uint_val, min); | |
c6c8fea2 SE |
397 | return -EINVAL; |
398 | } | |
399 | ||
400 | if (uint_val > max) { | |
3e34819e SE |
401 | batadv_info(net_dev, "%s: Value is too big: %lu max: %u\n", |
402 | attr_name, uint_val, max); | |
c6c8fea2 SE |
403 | return -EINVAL; |
404 | } | |
405 | ||
406 | if (atomic_read(attr) == uint_val) | |
407 | return count; | |
408 | ||
a25bab9d SE |
409 | if (slave_dev) |
410 | snprintf(ifname, sizeof(ifname), "%s: ", slave_dev->name); | |
411 | ||
412 | batadv_info(net_dev, "%s: %sChanging from: %i to: %lu\n", | |
413 | attr_name, ifname, atomic_read(attr), uint_val); | |
c6c8fea2 SE |
414 | |
415 | atomic_set(attr, uint_val); | |
416 | return count; | |
417 | } | |
418 | ||
c149ca72 AQ |
419 | static ssize_t __batadv_store_uint_attr(const char *buff, size_t count, |
420 | int min, int max, | |
421 | void (*post_func)(struct net_device *), | |
422 | const struct attribute *attr, | |
423 | atomic_t *attr_store, | |
a25bab9d SE |
424 | struct net_device *net_dev, |
425 | struct net_device *slave_dev) | |
c6c8fea2 SE |
426 | { |
427 | int ret; | |
428 | ||
a25bab9d SE |
429 | ret = batadv_store_uint_attr(buff, count, net_dev, slave_dev, |
430 | attr->name, min, max, attr_store); | |
c6c8fea2 SE |
431 | if (post_func && ret) |
432 | post_func(net_dev); | |
433 | ||
434 | return ret; | |
435 | } | |
436 | ||
0ff9b86f SE |
437 | static ssize_t batadv_show_bat_algo(struct kobject *kobj, |
438 | struct attribute *attr, char *buff) | |
ea3d2fd1 | 439 | { |
56303d34 | 440 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); |
f138694b | 441 | |
1392f553 | 442 | batadv_sysfs_deprecated(attr); |
29824a55 | 443 | return sprintf(buff, "%s\n", bat_priv->algo_ops->name); |
ea3d2fd1 ML |
444 | } |
445 | ||
4e820e72 | 446 | static void batadv_post_gw_reselect(struct net_device *net_dev) |
c6c8fea2 | 447 | { |
56303d34 | 448 | struct batadv_priv *bat_priv = netdev_priv(net_dev); |
f138694b | 449 | |
4e820e72 | 450 | batadv_gw_reselect(bat_priv); |
c6c8fea2 SE |
451 | } |
452 | ||
0ff9b86f SE |
453 | static ssize_t batadv_show_gw_mode(struct kobject *kobj, struct attribute *attr, |
454 | char *buff) | |
c6c8fea2 | 455 | { |
56303d34 | 456 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); |
c6c8fea2 SE |
457 | int bytes_written; |
458 | ||
1392f553 SE |
459 | batadv_sysfs_deprecated(attr); |
460 | ||
a8d8d1de AQ |
461 | /* GW mode is not available if the routing algorithm in use does not |
462 | * implement the GW API | |
463 | */ | |
464 | if (!bat_priv->algo_ops->gw.get_best_gw_node || | |
465 | !bat_priv->algo_ops->gw.is_eligible) | |
466 | return -ENOENT; | |
467 | ||
3a24a63e | 468 | switch (atomic_read(&bat_priv->gw.mode)) { |
cd646ab1 | 469 | case BATADV_GW_MODE_CLIENT: |
97ea4ba1 SE |
470 | bytes_written = sprintf(buff, "%s\n", |
471 | BATADV_GW_MODE_CLIENT_NAME); | |
c6c8fea2 | 472 | break; |
cd646ab1 | 473 | case BATADV_GW_MODE_SERVER: |
97ea4ba1 SE |
474 | bytes_written = sprintf(buff, "%s\n", |
475 | BATADV_GW_MODE_SERVER_NAME); | |
c6c8fea2 SE |
476 | break; |
477 | default: | |
97ea4ba1 SE |
478 | bytes_written = sprintf(buff, "%s\n", |
479 | BATADV_GW_MODE_OFF_NAME); | |
c6c8fea2 SE |
480 | break; |
481 | } | |
482 | ||
483 | return bytes_written; | |
484 | } | |
485 | ||
0ff9b86f SE |
486 | static ssize_t batadv_store_gw_mode(struct kobject *kobj, |
487 | struct attribute *attr, char *buff, | |
488 | size_t count) | |
c6c8fea2 | 489 | { |
0ff9b86f | 490 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); |
56303d34 | 491 | struct batadv_priv *bat_priv = netdev_priv(net_dev); |
c6c8fea2 SE |
492 | char *curr_gw_mode_str; |
493 | int gw_mode_tmp = -1; | |
494 | ||
1392f553 SE |
495 | batadv_sysfs_deprecated(attr); |
496 | ||
a8d8d1de AQ |
497 | /* toggling GW mode is allowed only if the routing algorithm in use |
498 | * provides the GW API | |
499 | */ | |
500 | if (!bat_priv->algo_ops->gw.get_best_gw_node || | |
501 | !bat_priv->algo_ops->gw.is_eligible) | |
502 | return -EINVAL; | |
503 | ||
c6c8fea2 SE |
504 | if (buff[count - 1] == '\n') |
505 | buff[count - 1] = '\0'; | |
506 | ||
97ea4ba1 SE |
507 | if (strncmp(buff, BATADV_GW_MODE_OFF_NAME, |
508 | strlen(BATADV_GW_MODE_OFF_NAME)) == 0) | |
cd646ab1 | 509 | gw_mode_tmp = BATADV_GW_MODE_OFF; |
c6c8fea2 | 510 | |
97ea4ba1 SE |
511 | if (strncmp(buff, BATADV_GW_MODE_CLIENT_NAME, |
512 | strlen(BATADV_GW_MODE_CLIENT_NAME)) == 0) | |
cd646ab1 | 513 | gw_mode_tmp = BATADV_GW_MODE_CLIENT; |
c6c8fea2 | 514 | |
97ea4ba1 SE |
515 | if (strncmp(buff, BATADV_GW_MODE_SERVER_NAME, |
516 | strlen(BATADV_GW_MODE_SERVER_NAME)) == 0) | |
cd646ab1 | 517 | gw_mode_tmp = BATADV_GW_MODE_SERVER; |
c6c8fea2 SE |
518 | |
519 | if (gw_mode_tmp < 0) { | |
3e34819e SE |
520 | batadv_info(net_dev, |
521 | "Invalid parameter for 'gw mode' setting received: %s\n", | |
522 | buff); | |
c6c8fea2 SE |
523 | return -EINVAL; |
524 | } | |
525 | ||
3a24a63e | 526 | if (atomic_read(&bat_priv->gw.mode) == gw_mode_tmp) |
c6c8fea2 SE |
527 | return count; |
528 | ||
3a24a63e | 529 | switch (atomic_read(&bat_priv->gw.mode)) { |
cd646ab1 | 530 | case BATADV_GW_MODE_CLIENT: |
97ea4ba1 | 531 | curr_gw_mode_str = BATADV_GW_MODE_CLIENT_NAME; |
c6c8fea2 | 532 | break; |
cd646ab1 | 533 | case BATADV_GW_MODE_SERVER: |
97ea4ba1 | 534 | curr_gw_mode_str = BATADV_GW_MODE_SERVER_NAME; |
c6c8fea2 SE |
535 | break; |
536 | default: | |
97ea4ba1 | 537 | curr_gw_mode_str = BATADV_GW_MODE_OFF_NAME; |
c6c8fea2 SE |
538 | break; |
539 | } | |
540 | ||
3e34819e SE |
541 | batadv_info(net_dev, "Changing gw mode from: %s to: %s\n", |
542 | curr_gw_mode_str, buff); | |
c6c8fea2 | 543 | |
f3163181 AQ |
544 | /* Invoking batadv_gw_reselect() is not enough to really de-select the |
545 | * current GW. It will only instruct the gateway client code to perform | |
546 | * a re-election the next time that this is needed. | |
547 | * | |
548 | * When gw client mode is being switched off the current GW must be | |
549 | * de-selected explicitly otherwise no GW_ADD uevent is thrown on | |
550 | * client mode re-activation. This is operation is performed in | |
551 | * batadv_gw_check_client_stop(). | |
552 | */ | |
4e820e72 | 553 | batadv_gw_reselect(bat_priv); |
c6eaa3f0 AQ |
554 | /* always call batadv_gw_check_client_stop() before changing the gateway |
555 | * state | |
556 | */ | |
557 | batadv_gw_check_client_stop(bat_priv); | |
3a24a63e | 558 | atomic_set(&bat_priv->gw.mode, (unsigned int)gw_mode_tmp); |
414254e3 | 559 | batadv_gw_tvlv_container_update(bat_priv); |
7e6f461e SE |
560 | |
561 | batadv_netlink_notify_mesh(bat_priv); | |
562 | ||
c6c8fea2 SE |
563 | return count; |
564 | } | |
565 | ||
08686943 AQ |
566 | static ssize_t batadv_show_gw_sel_class(struct kobject *kobj, |
567 | struct attribute *attr, char *buff) | |
568 | { | |
569 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); | |
570 | ||
1392f553 SE |
571 | batadv_sysfs_deprecated(attr); |
572 | ||
a8d8d1de AQ |
573 | /* GW selection class is not available if the routing algorithm in use |
574 | * does not implement the GW API | |
575 | */ | |
576 | if (!bat_priv->algo_ops->gw.get_best_gw_node || | |
577 | !bat_priv->algo_ops->gw.is_eligible) | |
578 | return -ENOENT; | |
579 | ||
08686943 AQ |
580 | if (bat_priv->algo_ops->gw.show_sel_class) |
581 | return bat_priv->algo_ops->gw.show_sel_class(bat_priv, buff); | |
582 | ||
583 | return sprintf(buff, "%i\n", atomic_read(&bat_priv->gw.sel_class)); | |
584 | } | |
585 | ||
586 | static ssize_t batadv_store_gw_sel_class(struct kobject *kobj, | |
587 | struct attribute *attr, char *buff, | |
588 | size_t count) | |
589 | { | |
590 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); | |
7e6f461e | 591 | ssize_t length; |
08686943 | 592 | |
1392f553 SE |
593 | batadv_sysfs_deprecated(attr); |
594 | ||
a8d8d1de AQ |
595 | /* setting the GW selection class is allowed only if the routing |
596 | * algorithm in use implements the GW API | |
597 | */ | |
598 | if (!bat_priv->algo_ops->gw.get_best_gw_node || | |
599 | !bat_priv->algo_ops->gw.is_eligible) | |
600 | return -EINVAL; | |
601 | ||
08686943 AQ |
602 | if (buff[count - 1] == '\n') |
603 | buff[count - 1] = '\0'; | |
604 | ||
605 | if (bat_priv->algo_ops->gw.store_sel_class) | |
606 | return bat_priv->algo_ops->gw.store_sel_class(bat_priv, buff, | |
607 | count); | |
608 | ||
7e6f461e SE |
609 | length = __batadv_store_uint_attr(buff, count, 1, BATADV_TQ_MAX_VALUE, |
610 | batadv_post_gw_reselect, attr, | |
611 | &bat_priv->gw.sel_class, | |
612 | bat_priv->soft_iface, NULL); | |
613 | ||
614 | batadv_netlink_notify_mesh(bat_priv); | |
615 | ||
616 | return length; | |
08686943 AQ |
617 | } |
618 | ||
0ff9b86f SE |
619 | static ssize_t batadv_show_gw_bwidth(struct kobject *kobj, |
620 | struct attribute *attr, char *buff) | |
c6c8fea2 | 621 | { |
56303d34 | 622 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); |
6b5e971a | 623 | u32 down, up; |
414254e3 | 624 | |
1392f553 SE |
625 | batadv_sysfs_deprecated(attr); |
626 | ||
414254e3 ML |
627 | down = atomic_read(&bat_priv->gw.bandwidth_down); |
628 | up = atomic_read(&bat_priv->gw.bandwidth_up); | |
629 | ||
630 | return sprintf(buff, "%u.%u/%u.%u MBit\n", down / 10, | |
631 | down % 10, up / 10, up % 10); | |
c6c8fea2 SE |
632 | } |
633 | ||
0ff9b86f SE |
634 | static ssize_t batadv_store_gw_bwidth(struct kobject *kobj, |
635 | struct attribute *attr, char *buff, | |
636 | size_t count) | |
c6c8fea2 | 637 | { |
7e6f461e | 638 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); |
0ff9b86f | 639 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); |
7e6f461e | 640 | ssize_t length; |
c6c8fea2 | 641 | |
1392f553 SE |
642 | batadv_sysfs_deprecated(attr); |
643 | ||
c6c8fea2 SE |
644 | if (buff[count - 1] == '\n') |
645 | buff[count - 1] = '\0'; | |
646 | ||
7e6f461e SE |
647 | length = batadv_gw_bandwidth_set(net_dev, buff, count); |
648 | ||
649 | batadv_netlink_notify_mesh(bat_priv); | |
650 | ||
651 | return length; | |
c6c8fea2 SE |
652 | } |
653 | ||
c42edfe3 | 654 | /** |
7e9a8c2c | 655 | * batadv_show_isolation_mark() - print the current isolation mark/mask |
c42edfe3 AQ |
656 | * @kobj: kobject representing the private mesh sysfs directory |
657 | * @attr: the batman-adv attribute the user is interacting with | |
658 | * @buff: the buffer that will contain the data to send back to the user | |
659 | * | |
62fe710f | 660 | * Return: the number of bytes written into 'buff' on success or a negative |
c42edfe3 AQ |
661 | * error code in case of failure |
662 | */ | |
663 | static ssize_t batadv_show_isolation_mark(struct kobject *kobj, | |
664 | struct attribute *attr, char *buff) | |
665 | { | |
666 | struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); | |
667 | ||
1392f553 | 668 | batadv_sysfs_deprecated(attr); |
c42edfe3 AQ |
669 | return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->isolation_mark, |
670 | bat_priv->isolation_mark_mask); | |
671 | } | |
672 | ||
673 | /** | |
7e9a8c2c SE |
674 | * batadv_store_isolation_mark() - parse and store the isolation mark/mask |
675 | * entered by the user | |
c42edfe3 AQ |
676 | * @kobj: kobject representing the private mesh sysfs directory |
677 | * @attr: the batman-adv attribute the user is interacting with | |
678 | * @buff: the buffer containing the user data | |
679 | * @count: number of bytes in the buffer | |
680 | * | |
62fe710f | 681 | * Return: 'count' on success or a negative error code in case of failure |
c42edfe3 AQ |
682 | */ |
683 | static ssize_t batadv_store_isolation_mark(struct kobject *kobj, | |
684 | struct attribute *attr, char *buff, | |
685 | size_t count) | |
686 | { | |
687 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); | |
688 | struct batadv_priv *bat_priv = netdev_priv(net_dev); | |
6b5e971a | 689 | u32 mark, mask; |
c42edfe3 AQ |
690 | char *mask_ptr; |
691 | ||
1392f553 SE |
692 | batadv_sysfs_deprecated(attr); |
693 | ||
c42edfe3 AQ |
694 | /* parse the mask if it has been specified, otherwise assume the mask is |
695 | * the biggest possible | |
696 | */ | |
697 | mask = 0xFFFFFFFF; | |
698 | mask_ptr = strchr(buff, '/'); | |
699 | if (mask_ptr) { | |
700 | *mask_ptr = '\0'; | |
701 | mask_ptr++; | |
702 | ||
703 | /* the mask must be entered in hex base as it is going to be a | |
704 | * bitmask and not a prefix length | |
705 | */ | |
706 | if (kstrtou32(mask_ptr, 16, &mask) < 0) | |
707 | return -EINVAL; | |
708 | } | |
709 | ||
710 | /* the mark can be entered in any base */ | |
711 | if (kstrtou32(buff, 0, &mark) < 0) | |
712 | return -EINVAL; | |
713 | ||
714 | bat_priv->isolation_mark_mask = mask; | |
715 | /* erase bits not covered by the mask */ | |
716 | bat_priv->isolation_mark = mark & bat_priv->isolation_mark_mask; | |
717 | ||
718 | batadv_info(net_dev, | |
719 | "New skb mark for extended isolation: %#.8x/%#.8x\n", | |
720 | bat_priv->isolation_mark, bat_priv->isolation_mark_mask); | |
721 | ||
7e6f461e SE |
722 | batadv_netlink_notify_mesh(bat_priv); |
723 | ||
c42edfe3 AQ |
724 | return count; |
725 | } | |
726 | ||
507b37cf SE |
727 | BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL); |
728 | BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL); | |
7a5cc242 | 729 | #ifdef CONFIG_BATMAN_ADV_BLA |
507b37cf | 730 | BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, 0644, batadv_bla_status_update); |
7a5cc242 | 731 | #endif |
33af49ad | 732 | #ifdef CONFIG_BATMAN_ADV_DAT |
507b37cf | 733 | BATADV_ATTR_SIF_BOOL(distributed_arp_table, 0644, batadv_dat_status_update); |
33af49ad | 734 | #endif |
507b37cf SE |
735 | BATADV_ATTR_SIF_BOOL(fragmentation, 0644, batadv_update_min_mtu); |
736 | static BATADV_ATTR(routing_algo, 0444, batadv_show_bat_algo, NULL); | |
737 | static BATADV_ATTR(gw_mode, 0644, batadv_show_gw_mode, batadv_store_gw_mode); | |
738 | BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, 0644, 2 * BATADV_JITTER, | |
739 | INT_MAX, NULL); | |
740 | BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, 0644, 0, BATADV_TQ_MAX_VALUE, | |
741 | NULL); | |
742 | static BATADV_ATTR(gw_sel_class, 0644, batadv_show_gw_sel_class, | |
08686943 | 743 | batadv_store_gw_sel_class); |
507b37cf | 744 | static BATADV_ATTR(gw_bandwidth, 0644, batadv_show_gw_bwidth, |
347c80f0 | 745 | batadv_store_gw_bwidth); |
1d8ab8d3 | 746 | #ifdef CONFIG_BATMAN_ADV_MCAST |
507b37cf | 747 | BATADV_ATTR_SIF_BOOL(multicast_mode, 0644, NULL); |
1d8ab8d3 | 748 | #endif |
c6c8fea2 | 749 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
507b37cf | 750 | BATADV_ATTR_SIF_UINT(log_level, log_level, 0644, 0, BATADV_DBG_ALL, NULL); |
c6c8fea2 | 751 | #endif |
d353d8d4 | 752 | #ifdef CONFIG_BATMAN_ADV_NC |
507b37cf | 753 | BATADV_ATTR_SIF_BOOL(network_coding, 0644, batadv_nc_status_update); |
d353d8d4 | 754 | #endif |
507b37cf SE |
755 | static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark, |
756 | batadv_store_isolation_mark); | |
c6c8fea2 | 757 | |
b4d66b87 | 758 | static struct batadv_attribute *batadv_mesh_attrs[] = { |
0ff9b86f SE |
759 | &batadv_attr_aggregated_ogms, |
760 | &batadv_attr_bonding, | |
7a5cc242 | 761 | #ifdef CONFIG_BATMAN_ADV_BLA |
0ff9b86f | 762 | &batadv_attr_bridge_loop_avoidance, |
33af49ad AQ |
763 | #endif |
764 | #ifdef CONFIG_BATMAN_ADV_DAT | |
765 | &batadv_attr_distributed_arp_table, | |
1d8ab8d3 LL |
766 | #endif |
767 | #ifdef CONFIG_BATMAN_ADV_MCAST | |
768 | &batadv_attr_multicast_mode, | |
7a5cc242 | 769 | #endif |
0ff9b86f | 770 | &batadv_attr_fragmentation, |
0ff9b86f SE |
771 | &batadv_attr_routing_algo, |
772 | &batadv_attr_gw_mode, | |
773 | &batadv_attr_orig_interval, | |
774 | &batadv_attr_hop_penalty, | |
775 | &batadv_attr_gw_sel_class, | |
776 | &batadv_attr_gw_bandwidth, | |
c6c8fea2 | 777 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
0ff9b86f | 778 | &batadv_attr_log_level, |
d353d8d4 MH |
779 | #endif |
780 | #ifdef CONFIG_BATMAN_ADV_NC | |
781 | &batadv_attr_network_coding, | |
c6c8fea2 | 782 | #endif |
c42edfe3 | 783 | &batadv_attr_isolation_mark, |
c6c8fea2 SE |
784 | NULL, |
785 | }; | |
786 | ||
507b37cf | 787 | BATADV_ATTR_VLAN_BOOL(ap_isolation, 0644, NULL); |
b8cbd81d | 788 | |
f34ac9d4 | 789 | /* array of vlan specific sysfs attributes */ |
90f4435d | 790 | static struct batadv_attribute *batadv_vlan_attrs[] = { |
b8cbd81d | 791 | &batadv_attr_vlan_ap_isolation, |
90f4435d AQ |
792 | NULL, |
793 | }; | |
794 | ||
ff15c27c SE |
795 | /** |
796 | * batadv_sysfs_add_meshif() - Add soft interface specific sysfs entries | |
797 | * @dev: netdev struct of the soft interface | |
798 | * | |
799 | * Return: 0 on success or negative error number in case of failure | |
800 | */ | |
5853e22c | 801 | int batadv_sysfs_add_meshif(struct net_device *dev) |
c6c8fea2 SE |
802 | { |
803 | struct kobject *batif_kobject = &dev->dev.kobj; | |
56303d34 | 804 | struct batadv_priv *bat_priv = netdev_priv(dev); |
b4d66b87 | 805 | struct batadv_attribute **bat_attr; |
c6c8fea2 SE |
806 | int err; |
807 | ||
036cbfeb | 808 | bat_priv->mesh_obj = kobject_create_and_add(BATADV_SYSFS_IF_MESH_SUBDIR, |
c6c8fea2 SE |
809 | batif_kobject); |
810 | if (!bat_priv->mesh_obj) { | |
3e34819e | 811 | batadv_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, |
036cbfeb | 812 | BATADV_SYSFS_IF_MESH_SUBDIR); |
c6c8fea2 SE |
813 | goto out; |
814 | } | |
815 | ||
0ff9b86f | 816 | for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr) { |
c6c8fea2 SE |
817 | err = sysfs_create_file(bat_priv->mesh_obj, |
818 | &((*bat_attr)->attr)); | |
819 | if (err) { | |
3e34819e | 820 | batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n", |
036cbfeb | 821 | dev->name, BATADV_SYSFS_IF_MESH_SUBDIR, |
3e34819e | 822 | ((*bat_attr)->attr).name); |
c6c8fea2 SE |
823 | goto rem_attr; |
824 | } | |
825 | } | |
826 | ||
827 | return 0; | |
828 | ||
829 | rem_attr: | |
0ff9b86f | 830 | for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr) |
c6c8fea2 SE |
831 | sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); |
832 | ||
f4acb108 SE |
833 | kobject_uevent(bat_priv->mesh_obj, KOBJ_REMOVE); |
834 | kobject_del(bat_priv->mesh_obj); | |
c6c8fea2 SE |
835 | kobject_put(bat_priv->mesh_obj); |
836 | bat_priv->mesh_obj = NULL; | |
837 | out: | |
838 | return -ENOMEM; | |
839 | } | |
840 | ||
ff15c27c SE |
841 | /** |
842 | * batadv_sysfs_del_meshif() - Remove soft interface specific sysfs entries | |
843 | * @dev: netdev struct of the soft interface | |
844 | */ | |
5853e22c | 845 | void batadv_sysfs_del_meshif(struct net_device *dev) |
c6c8fea2 | 846 | { |
56303d34 | 847 | struct batadv_priv *bat_priv = netdev_priv(dev); |
b4d66b87 | 848 | struct batadv_attribute **bat_attr; |
c6c8fea2 | 849 | |
0ff9b86f | 850 | for (bat_attr = batadv_mesh_attrs; *bat_attr; ++bat_attr) |
c6c8fea2 SE |
851 | sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); |
852 | ||
f4acb108 SE |
853 | kobject_uevent(bat_priv->mesh_obj, KOBJ_REMOVE); |
854 | kobject_del(bat_priv->mesh_obj); | |
c6c8fea2 SE |
855 | kobject_put(bat_priv->mesh_obj); |
856 | bat_priv->mesh_obj = NULL; | |
857 | } | |
858 | ||
90f4435d | 859 | /** |
7e9a8c2c | 860 | * batadv_sysfs_add_vlan() - add all the needed sysfs objects for the new vlan |
90f4435d AQ |
861 | * @dev: netdev of the mesh interface |
862 | * @vlan: private data of the newly added VLAN interface | |
863 | * | |
62fe710f | 864 | * Return: 0 on success and -ENOMEM if any of the structure allocations fails. |
90f4435d AQ |
865 | */ |
866 | int batadv_sysfs_add_vlan(struct net_device *dev, | |
867 | struct batadv_softif_vlan *vlan) | |
868 | { | |
869 | char vlan_subdir[sizeof(BATADV_SYSFS_VLAN_SUBDIR_PREFIX) + 5]; | |
870 | struct batadv_priv *bat_priv = netdev_priv(dev); | |
871 | struct batadv_attribute **bat_attr; | |
872 | int err; | |
873 | ||
874 | if (vlan->vid & BATADV_VLAN_HAS_TAG) { | |
875 | sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%hu", | |
876 | vlan->vid & VLAN_VID_MASK); | |
877 | ||
878 | vlan->kobj = kobject_create_and_add(vlan_subdir, | |
879 | bat_priv->mesh_obj); | |
880 | if (!vlan->kobj) { | |
881 | batadv_err(dev, "Can't add sysfs directory: %s/%s\n", | |
882 | dev->name, vlan_subdir); | |
883 | goto out; | |
884 | } | |
885 | } else { | |
886 | /* the untagged LAN uses the root folder to store its "VLAN | |
887 | * specific attributes" | |
888 | */ | |
889 | vlan->kobj = bat_priv->mesh_obj; | |
890 | kobject_get(bat_priv->mesh_obj); | |
891 | } | |
892 | ||
893 | for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) { | |
894 | err = sysfs_create_file(vlan->kobj, | |
895 | &((*bat_attr)->attr)); | |
896 | if (err) { | |
897 | batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n", | |
898 | dev->name, vlan_subdir, | |
899 | ((*bat_attr)->attr).name); | |
900 | goto rem_attr; | |
901 | } | |
902 | } | |
903 | ||
904 | return 0; | |
905 | ||
906 | rem_attr: | |
907 | for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) | |
908 | sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr)); | |
909 | ||
f4acb108 SE |
910 | if (vlan->kobj != bat_priv->mesh_obj) { |
911 | kobject_uevent(vlan->kobj, KOBJ_REMOVE); | |
912 | kobject_del(vlan->kobj); | |
913 | } | |
90f4435d AQ |
914 | kobject_put(vlan->kobj); |
915 | vlan->kobj = NULL; | |
916 | out: | |
917 | return -ENOMEM; | |
918 | } | |
919 | ||
920 | /** | |
7e9a8c2c | 921 | * batadv_sysfs_del_vlan() - remove all the sysfs objects for a given VLAN |
90f4435d AQ |
922 | * @bat_priv: the bat priv with all the soft interface information |
923 | * @vlan: the private data of the VLAN to destroy | |
924 | */ | |
925 | void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv, | |
926 | struct batadv_softif_vlan *vlan) | |
927 | { | |
928 | struct batadv_attribute **bat_attr; | |
929 | ||
930 | for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) | |
931 | sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr)); | |
932 | ||
f4acb108 SE |
933 | if (vlan->kobj != bat_priv->mesh_obj) { |
934 | kobject_uevent(vlan->kobj, KOBJ_REMOVE); | |
935 | kobject_del(vlan->kobj); | |
936 | } | |
90f4435d AQ |
937 | kobject_put(vlan->kobj); |
938 | vlan->kobj = NULL; | |
939 | } | |
940 | ||
0ff9b86f SE |
941 | static ssize_t batadv_show_mesh_iface(struct kobject *kobj, |
942 | struct attribute *attr, char *buff) | |
c6c8fea2 | 943 | { |
0ff9b86f | 944 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); |
56303d34 | 945 | struct batadv_hard_iface *hard_iface; |
c6c8fea2 | 946 | ssize_t length; |
e9a4f295 | 947 | const char *ifname; |
c6c8fea2 | 948 | |
1392f553 SE |
949 | batadv_sysfs_deprecated(attr); |
950 | ||
56303d34 | 951 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
e6c10f43 | 952 | if (!hard_iface) |
c6c8fea2 SE |
953 | return 0; |
954 | ||
e9a4f295 SE |
955 | if (hard_iface->if_status == BATADV_IF_NOT_IN_USE) |
956 | ifname = "none"; | |
957 | else | |
958 | ifname = hard_iface->soft_iface->name; | |
959 | ||
960 | length = sprintf(buff, "%s\n", ifname); | |
c6c8fea2 | 961 | |
82047ad7 | 962 | batadv_hardif_put(hard_iface); |
c6c8fea2 SE |
963 | |
964 | return length; | |
965 | } | |
966 | ||
77d69d8c | 967 | /** |
7e9a8c2c | 968 | * batadv_store_mesh_iface_finish() - store new hardif mesh_iface state |
77d69d8c SE |
969 | * @net_dev: netdevice to add/remove to/from batman-adv soft-interface |
970 | * @ifname: name of soft-interface to modify | |
971 | * | |
972 | * Changes the parts of the hard+soft interface which can not be modified under | |
973 | * sysfs lock (to prevent deadlock situations). | |
974 | * | |
975 | * Return: 0 on success, 0 < on failure | |
976 | */ | |
977 | static int batadv_store_mesh_iface_finish(struct net_device *net_dev, | |
978 | char ifname[IFNAMSIZ]) | |
c6c8fea2 | 979 | { |
2cd45a06 | 980 | struct net *net = dev_net(net_dev); |
56303d34 | 981 | struct batadv_hard_iface *hard_iface; |
77d69d8c SE |
982 | int status_tmp; |
983 | int ret = 0; | |
984 | ||
985 | ASSERT_RTNL(); | |
c6c8fea2 | 986 | |
56303d34 | 987 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
e6c10f43 | 988 | if (!hard_iface) |
77d69d8c | 989 | return 0; |
c6c8fea2 | 990 | |
77d69d8c | 991 | if (strncmp(ifname, "none", 4) == 0) |
e9a4f295 | 992 | status_tmp = BATADV_IF_NOT_IN_USE; |
c6c8fea2 | 993 | else |
e9a4f295 | 994 | status_tmp = BATADV_IF_I_WANT_YOU; |
c6c8fea2 | 995 | |
e6c10f43 ML |
996 | if (hard_iface->if_status == status_tmp) |
997 | goto out; | |
998 | ||
825ffe1f SE |
999 | if (hard_iface->soft_iface && |
1000 | strncmp(hard_iface->soft_iface->name, ifname, IFNAMSIZ) == 0) | |
ed75ccbe | 1001 | goto out; |
c6c8fea2 | 1002 | |
e9a4f295 | 1003 | if (status_tmp == BATADV_IF_NOT_IN_USE) { |
a15fd361 SE |
1004 | batadv_hardif_disable_interface(hard_iface, |
1005 | BATADV_IF_CLEANUP_AUTO); | |
77d69d8c | 1006 | goto out; |
c6c8fea2 SE |
1007 | } |
1008 | ||
1009 | /* if the interface already is in use */ | |
e9a4f295 | 1010 | if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) |
a15fd361 SE |
1011 | batadv_hardif_disable_interface(hard_iface, |
1012 | BATADV_IF_CLEANUP_AUTO); | |
c6c8fea2 | 1013 | |
77d69d8c | 1014 | ret = batadv_hardif_enable_interface(hard_iface, net, ifname); |
ed75ccbe | 1015 | out: |
82047ad7 | 1016 | batadv_hardif_put(hard_iface); |
c6c8fea2 SE |
1017 | return ret; |
1018 | } | |
1019 | ||
77d69d8c | 1020 | /** |
7e9a8c2c | 1021 | * batadv_store_mesh_iface_work() - store new hardif mesh_iface state |
77d69d8c SE |
1022 | * @work: work queue item |
1023 | * | |
1024 | * Changes the parts of the hard+soft interface which can not be modified under | |
1025 | * sysfs lock (to prevent deadlock situations). | |
1026 | */ | |
1027 | static void batadv_store_mesh_iface_work(struct work_struct *work) | |
1028 | { | |
1029 | struct batadv_store_mesh_work *store_work; | |
1030 | int ret; | |
1031 | ||
1032 | store_work = container_of(work, struct batadv_store_mesh_work, work); | |
1033 | ||
1034 | rtnl_lock(); | |
1035 | ret = batadv_store_mesh_iface_finish(store_work->net_dev, | |
1036 | store_work->soft_iface_name); | |
1037 | rtnl_unlock(); | |
1038 | ||
1039 | if (ret < 0) | |
1040 | pr_err("Failed to store new mesh_iface state %s for %s: %d\n", | |
1041 | store_work->soft_iface_name, store_work->net_dev->name, | |
1042 | ret); | |
1043 | ||
1044 | dev_put(store_work->net_dev); | |
1045 | kfree(store_work); | |
1046 | } | |
1047 | ||
1048 | static ssize_t batadv_store_mesh_iface(struct kobject *kobj, | |
1049 | struct attribute *attr, char *buff, | |
1050 | size_t count) | |
1051 | { | |
1052 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); | |
1053 | struct batadv_store_mesh_work *store_work; | |
1054 | ||
1392f553 SE |
1055 | batadv_sysfs_deprecated(attr); |
1056 | ||
77d69d8c SE |
1057 | if (buff[count - 1] == '\n') |
1058 | buff[count - 1] = '\0'; | |
1059 | ||
1060 | if (strlen(buff) >= IFNAMSIZ) { | |
1061 | pr_err("Invalid parameter for 'mesh_iface' setting received: interface name too long '%s'\n", | |
1062 | buff); | |
1063 | return -EINVAL; | |
1064 | } | |
1065 | ||
1066 | store_work = kmalloc(sizeof(*store_work), GFP_KERNEL); | |
1067 | if (!store_work) | |
1068 | return -ENOMEM; | |
1069 | ||
1070 | dev_hold(net_dev); | |
1071 | INIT_WORK(&store_work->work, batadv_store_mesh_iface_work); | |
1072 | store_work->net_dev = net_dev; | |
529a8f93 | 1073 | strscpy(store_work->soft_iface_name, buff, |
77d69d8c SE |
1074 | sizeof(store_work->soft_iface_name)); |
1075 | ||
1076 | queue_work(batadv_event_workqueue, &store_work->work); | |
1077 | ||
1078 | return count; | |
1079 | } | |
1080 | ||
0ff9b86f SE |
1081 | static ssize_t batadv_show_iface_status(struct kobject *kobj, |
1082 | struct attribute *attr, char *buff) | |
c6c8fea2 | 1083 | { |
0ff9b86f | 1084 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); |
56303d34 | 1085 | struct batadv_hard_iface *hard_iface; |
c6c8fea2 SE |
1086 | ssize_t length; |
1087 | ||
1392f553 SE |
1088 | batadv_sysfs_deprecated(attr); |
1089 | ||
56303d34 | 1090 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
e6c10f43 | 1091 | if (!hard_iface) |
c6c8fea2 SE |
1092 | return 0; |
1093 | ||
e6c10f43 | 1094 | switch (hard_iface->if_status) { |
e9a4f295 | 1095 | case BATADV_IF_TO_BE_REMOVED: |
c6c8fea2 SE |
1096 | length = sprintf(buff, "disabling\n"); |
1097 | break; | |
e9a4f295 | 1098 | case BATADV_IF_INACTIVE: |
c6c8fea2 SE |
1099 | length = sprintf(buff, "inactive\n"); |
1100 | break; | |
e9a4f295 | 1101 | case BATADV_IF_ACTIVE: |
c6c8fea2 SE |
1102 | length = sprintf(buff, "active\n"); |
1103 | break; | |
e9a4f295 | 1104 | case BATADV_IF_TO_BE_ACTIVATED: |
c6c8fea2 SE |
1105 | length = sprintf(buff, "enabling\n"); |
1106 | break; | |
e9a4f295 | 1107 | case BATADV_IF_NOT_IN_USE: |
c6c8fea2 SE |
1108 | default: |
1109 | length = sprintf(buff, "not in use\n"); | |
1110 | break; | |
1111 | } | |
1112 | ||
82047ad7 | 1113 | batadv_hardif_put(hard_iface); |
c6c8fea2 SE |
1114 | |
1115 | return length; | |
1116 | } | |
1117 | ||
0b5ecc68 AQ |
1118 | #ifdef CONFIG_BATMAN_ADV_BATMAN_V |
1119 | ||
1120 | /** | |
7e9a8c2c | 1121 | * batadv_store_throughput_override() - parse and store throughput override |
0b5ecc68 AQ |
1122 | * entered by the user |
1123 | * @kobj: kobject representing the private mesh sysfs directory | |
1124 | * @attr: the batman-adv attribute the user is interacting with | |
1125 | * @buff: the buffer containing the user data | |
1126 | * @count: number of bytes in the buffer | |
1127 | * | |
1128 | * Return: 'count' on success or a negative error code in case of failure | |
1129 | */ | |
1130 | static ssize_t batadv_store_throughput_override(struct kobject *kobj, | |
1131 | struct attribute *attr, | |
1132 | char *buff, size_t count) | |
1133 | { | |
1134 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); | |
1135 | struct batadv_hard_iface *hard_iface; | |
438b3d3f | 1136 | struct batadv_priv *bat_priv; |
0b5ecc68 AQ |
1137 | u32 tp_override; |
1138 | u32 old_tp_override; | |
1139 | bool ret; | |
1140 | ||
1392f553 SE |
1141 | batadv_sysfs_deprecated(attr); |
1142 | ||
0b5ecc68 AQ |
1143 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
1144 | if (!hard_iface) | |
1145 | return -EINVAL; | |
1146 | ||
1147 | if (buff[count - 1] == '\n') | |
1148 | buff[count - 1] = '\0'; | |
1149 | ||
1150 | ret = batadv_parse_throughput(net_dev, buff, "throughput_override", | |
1151 | &tp_override); | |
1152 | if (!ret) | |
1153 | return count; | |
1154 | ||
1155 | old_tp_override = atomic_read(&hard_iface->bat_v.throughput_override); | |
1156 | if (old_tp_override == tp_override) | |
1157 | goto out; | |
1158 | ||
b9fd14c2 SE |
1159 | batadv_info(hard_iface->soft_iface, |
1160 | "%s: %s: Changing from: %u.%u MBit to: %u.%u MBit\n", | |
1161 | "throughput_override", net_dev->name, | |
0b5ecc68 AQ |
1162 | old_tp_override / 10, old_tp_override % 10, |
1163 | tp_override / 10, tp_override % 10); | |
1164 | ||
1165 | atomic_set(&hard_iface->bat_v.throughput_override, tp_override); | |
1166 | ||
438b3d3f SE |
1167 | if (hard_iface->soft_iface) { |
1168 | bat_priv = netdev_priv(hard_iface->soft_iface); | |
1169 | batadv_netlink_notify_hardif(bat_priv, hard_iface); | |
1170 | } | |
7e6f461e | 1171 | |
0b5ecc68 AQ |
1172 | out: |
1173 | batadv_hardif_put(hard_iface); | |
1174 | return count; | |
1175 | } | |
1176 | ||
1177 | static ssize_t batadv_show_throughput_override(struct kobject *kobj, | |
1178 | struct attribute *attr, | |
1179 | char *buff) | |
1180 | { | |
1181 | struct net_device *net_dev = batadv_kobj_to_netdev(kobj); | |
1182 | struct batadv_hard_iface *hard_iface; | |
1183 | u32 tp_override; | |
1184 | ||
1392f553 SE |
1185 | batadv_sysfs_deprecated(attr); |
1186 | ||
0b5ecc68 AQ |
1187 | hard_iface = batadv_hardif_get_by_netdev(net_dev); |
1188 | if (!hard_iface) | |
1189 | return -EINVAL; | |
1190 | ||
1191 | tp_override = atomic_read(&hard_iface->bat_v.throughput_override); | |
1192 | ||
1193 | return sprintf(buff, "%u.%u MBit\n", tp_override / 10, | |
1194 | tp_override % 10); | |
1195 | } | |
1196 | ||
1197 | #endif | |
1198 | ||
507b37cf | 1199 | static BATADV_ATTR(mesh_iface, 0644, batadv_show_mesh_iface, |
347c80f0 | 1200 | batadv_store_mesh_iface); |
507b37cf | 1201 | static BATADV_ATTR(iface_status, 0444, batadv_show_iface_status, NULL); |
7f136cd4 | 1202 | #ifdef CONFIG_BATMAN_ADV_BATMAN_V |
507b37cf | 1203 | BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, 0644, |
7f136cd4 | 1204 | 2 * BATADV_JITTER, INT_MAX, NULL); |
507b37cf | 1205 | static BATADV_ATTR(throughput_override, 0644, batadv_show_throughput_override, |
0b5ecc68 | 1206 | batadv_store_throughput_override); |
7f136cd4 | 1207 | #endif |
c6c8fea2 | 1208 | |
b4d66b87 | 1209 | static struct batadv_attribute *batadv_batman_attrs[] = { |
0ff9b86f SE |
1210 | &batadv_attr_mesh_iface, |
1211 | &batadv_attr_iface_status, | |
7f136cd4 LL |
1212 | #ifdef CONFIG_BATMAN_ADV_BATMAN_V |
1213 | &batadv_attr_elp_interval, | |
0b5ecc68 | 1214 | &batadv_attr_throughput_override, |
7f136cd4 | 1215 | #endif |
c6c8fea2 SE |
1216 | NULL, |
1217 | }; | |
1218 | ||
ff15c27c SE |
1219 | /** |
1220 | * batadv_sysfs_add_hardif() - Add hard interface specific sysfs entries | |
1221 | * @hardif_obj: address where to store the pointer to new sysfs folder | |
1222 | * @dev: netdev struct of the hard interface | |
1223 | * | |
1224 | * Return: 0 on success or negative error number in case of failure | |
1225 | */ | |
5853e22c | 1226 | int batadv_sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev) |
c6c8fea2 SE |
1227 | { |
1228 | struct kobject *hardif_kobject = &dev->dev.kobj; | |
b4d66b87 | 1229 | struct batadv_attribute **bat_attr; |
c6c8fea2 SE |
1230 | int err; |
1231 | ||
036cbfeb SE |
1232 | *hardif_obj = kobject_create_and_add(BATADV_SYSFS_IF_BAT_SUBDIR, |
1233 | hardif_kobject); | |
c6c8fea2 SE |
1234 | |
1235 | if (!*hardif_obj) { | |
3e34819e | 1236 | batadv_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, |
036cbfeb | 1237 | BATADV_SYSFS_IF_BAT_SUBDIR); |
c6c8fea2 SE |
1238 | goto out; |
1239 | } | |
1240 | ||
0ff9b86f | 1241 | for (bat_attr = batadv_batman_attrs; *bat_attr; ++bat_attr) { |
c6c8fea2 SE |
1242 | err = sysfs_create_file(*hardif_obj, &((*bat_attr)->attr)); |
1243 | if (err) { | |
3e34819e | 1244 | batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n", |
036cbfeb | 1245 | dev->name, BATADV_SYSFS_IF_BAT_SUBDIR, |
3e34819e | 1246 | ((*bat_attr)->attr).name); |
c6c8fea2 SE |
1247 | goto rem_attr; |
1248 | } | |
1249 | } | |
1250 | ||
1251 | return 0; | |
1252 | ||
1253 | rem_attr: | |
0ff9b86f | 1254 | for (bat_attr = batadv_batman_attrs; *bat_attr; ++bat_attr) |
c6c8fea2 SE |
1255 | sysfs_remove_file(*hardif_obj, &((*bat_attr)->attr)); |
1256 | out: | |
1257 | return -ENOMEM; | |
1258 | } | |
1259 | ||
ff15c27c SE |
1260 | /** |
1261 | * batadv_sysfs_del_hardif() - Remove hard interface specific sysfs entries | |
1262 | * @hardif_obj: address to the pointer to which stores batman-adv sysfs folder | |
1263 | * of the hard interface | |
1264 | */ | |
5853e22c | 1265 | void batadv_sysfs_del_hardif(struct kobject **hardif_obj) |
c6c8fea2 | 1266 | { |
f4acb108 SE |
1267 | kobject_uevent(*hardif_obj, KOBJ_REMOVE); |
1268 | kobject_del(*hardif_obj); | |
c6c8fea2 SE |
1269 | kobject_put(*hardif_obj); |
1270 | *hardif_obj = NULL; | |
1271 | } |