]>
Commit | Line | Data |
---|---|---|
5beef3c9 | 1 | /* |
9b6d10b7 | 2 | * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: |
5beef3c9 AL |
3 | * |
4 | * Marek Lindner, Simon Wunderlich | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of version 2 of the GNU General Public | |
8 | * License as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
18 | * 02110-1301, USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include "main.h" | |
23 | #include "proc.h" | |
5beef3c9 AL |
24 | #include "routing.h" |
25 | #include "translation-table.h" | |
26 | #include "hard-interface.h" | |
27 | #include "types.h" | |
28 | #include "hash.h" | |
29 | #include "vis.h" | |
5beef3c9 | 30 | |
5beef3c9 | 31 | static struct proc_dir_entry *proc_batman_dir, *proc_interface_file; |
47fdf097 | 32 | static struct proc_dir_entry *proc_orig_interval_file; |
e9b76450 | 33 | static struct proc_dir_entry *proc_vis_srv_file, *proc_vis_data_file; |
5beef3c9 AL |
34 | |
35 | static int proc_interfaces_read(struct seq_file *seq, void *offset) | |
36 | { | |
37 | struct batman_if *batman_if; | |
38 | ||
39 | rcu_read_lock(); | |
40 | list_for_each_entry_rcu(batman_if, &if_list, list) { | |
f6497e38 | 41 | seq_printf(seq, "[%8s] %s %s\n", |
5beef3c9 AL |
42 | (batman_if->if_active == IF_ACTIVE ? |
43 | "active" : "inactive"), | |
44 | batman_if->dev, | |
45 | (batman_if->if_active == IF_ACTIVE ? | |
46 | batman_if->addr_str : " ")); | |
47 | } | |
48 | rcu_read_unlock(); | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
53 | static int proc_interfaces_open(struct inode *inode, struct file *file) | |
54 | { | |
55 | return single_open(file, proc_interfaces_read, NULL); | |
56 | } | |
57 | ||
58 | static ssize_t proc_interfaces_write(struct file *instance, | |
59 | const char __user *userbuffer, | |
60 | size_t count, loff_t *data) | |
61 | { | |
62 | char *if_string, *colon_ptr = NULL, *cr_ptr = NULL; | |
af71b816 | 63 | int not_copied = 0, if_num = 0, add_success; |
5beef3c9 AL |
64 | struct batman_if *batman_if = NULL; |
65 | ||
66 | if_string = kmalloc(count, GFP_KERNEL); | |
67 | ||
68 | if (!if_string) | |
69 | return -ENOMEM; | |
70 | ||
71 | if (count > IFNAMSIZ - 1) { | |
bad2239e | 72 | printk(KERN_WARNING "batman-adv:Can't add interface: device name is too long\n"); |
5beef3c9 AL |
73 | goto end; |
74 | } | |
75 | ||
76 | not_copied = copy_from_user(if_string, userbuffer, count); | |
77 | if_string[count - not_copied - 1] = 0; | |
78 | ||
79 | colon_ptr = strchr(if_string, ':'); | |
80 | if (colon_ptr) | |
81 | *colon_ptr = 0; | |
82 | ||
83 | if (!colon_ptr) { | |
84 | cr_ptr = strchr(if_string, '\n'); | |
85 | if (cr_ptr) | |
86 | *cr_ptr = 0; | |
87 | } | |
88 | ||
89 | if (strlen(if_string) == 0) { | |
90 | shutdown_module(); | |
91 | num_ifs = 0; | |
92 | goto end; | |
93 | } | |
94 | ||
95 | /* add interface */ | |
96 | rcu_read_lock(); | |
97 | list_for_each_entry_rcu(batman_if, &if_list, list) { | |
98 | if (strncmp(batman_if->dev, if_string, count) == 0) { | |
bad2239e | 99 | printk(KERN_ERR "batman-adv:Given interface is already active: %s\n", if_string); |
5beef3c9 AL |
100 | rcu_read_unlock(); |
101 | goto end; | |
102 | ||
103 | } | |
104 | ||
105 | if_num++; | |
106 | } | |
107 | rcu_read_unlock(); | |
108 | ||
af71b816 ML |
109 | add_success = hardif_add_interface(if_string, if_num); |
110 | if (add_success < 0) | |
111 | goto end; | |
112 | ||
113 | num_ifs = if_num + 1; | |
5beef3c9 AL |
114 | |
115 | if ((atomic_read(&module_state) == MODULE_INACTIVE) && | |
116 | (hardif_get_active_if_num() > 0)) | |
117 | activate_module(); | |
118 | ||
5beef3c9 | 119 | return count; |
5beef3c9 AL |
120 | end: |
121 | kfree(if_string); | |
122 | return count; | |
123 | } | |
124 | ||
125 | static int proc_orig_interval_read(struct seq_file *seq, void *offset) | |
126 | { | |
127 | seq_printf(seq, "%i\n", atomic_read(&originator_interval)); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | static ssize_t proc_orig_interval_write(struct file *file, | |
133 | const char __user *buffer, | |
134 | size_t count, loff_t *ppos) | |
135 | { | |
136 | char *interval_string; | |
137 | int not_copied = 0; | |
138 | unsigned long originator_interval_tmp; | |
139 | int retval; | |
140 | ||
141 | interval_string = kmalloc(count, GFP_KERNEL); | |
142 | ||
143 | if (!interval_string) | |
144 | return -ENOMEM; | |
145 | ||
146 | not_copied = copy_from_user(interval_string, buffer, count); | |
147 | interval_string[count - not_copied - 1] = 0; | |
148 | ||
149 | retval = strict_strtoul(interval_string, 10, &originator_interval_tmp); | |
150 | if (retval) { | |
bad2239e | 151 | printk(KERN_ERR "batman-adv:New originator interval invalid\n"); |
5beef3c9 AL |
152 | goto end; |
153 | } | |
154 | ||
155 | if (originator_interval_tmp <= JITTER * 2) { | |
bad2239e AL |
156 | printk(KERN_WARNING "batman-adv:New originator interval too small: %li (min: %i)\n", |
157 | originator_interval_tmp, JITTER * 2); | |
5beef3c9 AL |
158 | goto end; |
159 | } | |
160 | ||
bad2239e AL |
161 | printk(KERN_INFO "batman-adv:Changing originator interval from: %i to: %li\n", |
162 | atomic_read(&originator_interval), originator_interval_tmp); | |
5beef3c9 AL |
163 | |
164 | atomic_set(&originator_interval, originator_interval_tmp); | |
165 | ||
166 | end: | |
167 | kfree(interval_string); | |
168 | return count; | |
169 | } | |
170 | ||
171 | static int proc_orig_interval_open(struct inode *inode, struct file *file) | |
172 | { | |
173 | return single_open(file, proc_orig_interval_read, NULL); | |
174 | } | |
175 | ||
e9b76450 ML |
176 | /* setting the mode of the vis server by the user */ |
177 | static ssize_t proc_vis_srv_write(struct file *file, const char __user * buffer, | |
178 | size_t count, loff_t *ppos) | |
5beef3c9 | 179 | { |
e9b76450 ML |
180 | char *vis_mode_string; |
181 | int not_copied = 0; | |
b801fede | 182 | |
e9b76450 | 183 | vis_mode_string = kmalloc(count, GFP_KERNEL); |
5beef3c9 | 184 | |
e9b76450 ML |
185 | if (!vis_mode_string) |
186 | return -ENOMEM; | |
187 | ||
188 | not_copied = copy_from_user(vis_mode_string, buffer, count); | |
189 | vis_mode_string[count - not_copied - 1] = 0; | |
190 | ||
191 | if ((strcmp(vis_mode_string, "client") == 0) || | |
192 | (strcmp(vis_mode_string, "disabled") == 0)) { | |
193 | printk(KERN_INFO "batman-adv:Setting VIS mode to client (disabling vis server)\n"); | |
837b8248 | 194 | atomic_set(&vis_mode, VIS_TYPE_CLIENT_UPDATE); |
e9b76450 ML |
195 | } else if ((strcmp(vis_mode_string, "server") == 0) || |
196 | (strcmp(vis_mode_string, "enabled") == 0)) { | |
197 | printk(KERN_INFO "batman-adv:Setting VIS mode to server (enabling vis server)\n"); | |
837b8248 | 198 | atomic_set(&vis_mode, VIS_TYPE_SERVER_SYNC); |
e9b76450 ML |
199 | } else |
200 | printk(KERN_ERR "batman-adv:Unknown VIS mode: %s\n", | |
201 | vis_mode_string); | |
202 | ||
203 | kfree(vis_mode_string); | |
204 | return count; | |
b801fede AL |
205 | } |
206 | ||
e9b76450 | 207 | static int proc_vis_srv_read(struct seq_file *seq, void *offset) |
b801fede | 208 | { |
837b8248 | 209 | int vis_server = atomic_read(&vis_mode); |
b801fede | 210 | |
f6497e38 | 211 | seq_printf(seq, "[%c] client mode (server disabled)\n", |
837b8248 | 212 | (vis_server == VIS_TYPE_CLIENT_UPDATE) ? 'x' : ' '); |
f6497e38 | 213 | seq_printf(seq, "[%c] server mode (server enabled)\n", |
837b8248 | 214 | (vis_server == VIS_TYPE_SERVER_SYNC) ? 'x' : ' '); |
b801fede | 215 | |
e9b76450 | 216 | return 0; |
5beef3c9 | 217 | } |
5beef3c9 | 218 | |
e9b76450 | 219 | static int proc_vis_srv_open(struct inode *inode, struct file *file) |
5beef3c9 | 220 | { |
e9b76450 | 221 | return single_open(file, proc_vis_srv_read, NULL); |
5beef3c9 AL |
222 | } |
223 | ||
e9b76450 | 224 | static int proc_vis_data_read(struct seq_file *seq, void *offset) |
5beef3c9 | 225 | { |
b6c35976 | 226 | HASHIT(hashit); |
5beef3c9 AL |
227 | struct vis_info *info; |
228 | struct vis_info_entry *entries; | |
b801fede | 229 | HLIST_HEAD(vis_if_list); |
f6497e38 LL |
230 | struct if_list_entry *entry; |
231 | struct hlist_node *pos, *n; | |
5beef3c9 | 232 | int i; |
5beef3c9 | 233 | char tmp_addr_str[ETH_STR_LEN]; |
e7017195 | 234 | unsigned long flags; |
837b8248 | 235 | int vis_server = atomic_read(&vis_mode); |
5beef3c9 | 236 | |
5beef3c9 | 237 | rcu_read_lock(); |
837b8248 | 238 | if (list_empty(&if_list) || (vis_server == VIS_TYPE_CLIENT_UPDATE)) { |
5beef3c9 | 239 | rcu_read_unlock(); |
5beef3c9 AL |
240 | goto end; |
241 | } | |
242 | ||
243 | rcu_read_unlock(); | |
244 | ||
e7017195 | 245 | spin_lock_irqsave(&vis_hash_lock, flags); |
b6c35976 SW |
246 | while (hash_iterate(vis_hash, &hashit)) { |
247 | info = hashit.bucket->data; | |
5beef3c9 AL |
248 | entries = (struct vis_info_entry *) |
249 | ((char *)info + sizeof(struct vis_info)); | |
250 | ||
251 | for (i = 0; i < info->packet.entries; i++) { | |
f6497e38 LL |
252 | if (entries[i].quality == 0) |
253 | continue; | |
254 | proc_vis_insert_interface(entries[i].src, &vis_if_list, | |
255 | compare_orig(entries[i].src, | |
256 | info->packet.vis_orig)); | |
5beef3c9 AL |
257 | } |
258 | ||
f6497e38 LL |
259 | hlist_for_each_entry(entry, pos, &vis_if_list, list) { |
260 | addr_to_string(tmp_addr_str, entry->addr); | |
261 | seq_printf(seq, "%s,", tmp_addr_str); | |
262 | ||
263 | for (i = 0; i < info->packet.entries; i++) | |
264 | proc_vis_read_entry(seq, &entries[i], | |
265 | entry->addr, entry->primary); | |
266 | ||
267 | /* add primary/secondary records */ | |
268 | if (compare_orig(entry->addr, info->packet.vis_orig)) | |
269 | proc_vis_read_prim_sec(seq, &vis_if_list); | |
270 | ||
271 | seq_printf(seq, "\n"); | |
272 | } | |
273 | ||
274 | hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, list) { | |
275 | hlist_del(&entry->list); | |
276 | kfree(entry); | |
277 | } | |
5beef3c9 | 278 | } |
e7017195 | 279 | spin_unlock_irqrestore(&vis_hash_lock, flags); |
5beef3c9 | 280 | |
5beef3c9 AL |
281 | end: |
282 | return 0; | |
283 | } | |
284 | ||
e9b76450 | 285 | static int proc_vis_data_open(struct inode *inode, struct file *file) |
5beef3c9 | 286 | { |
e9b76450 | 287 | return single_open(file, proc_vis_data_read, NULL); |
5beef3c9 AL |
288 | } |
289 | ||
5beef3c9 AL |
290 | /* satisfying different prototypes ... */ |
291 | static ssize_t proc_dummy_write(struct file *file, const char __user *buffer, | |
292 | size_t count, loff_t *ppos) | |
293 | { | |
294 | return count; | |
295 | } | |
296 | ||
e9b76450 | 297 | static const struct file_operations proc_vis_srv_fops = { |
5beef3c9 | 298 | .owner = THIS_MODULE, |
e9b76450 | 299 | .open = proc_vis_srv_open, |
5beef3c9 | 300 | .read = seq_read, |
e9b76450 ML |
301 | .write = proc_vis_srv_write, |
302 | .llseek = seq_lseek, | |
303 | .release = single_release, | |
304 | }; | |
305 | ||
306 | static const struct file_operations proc_vis_data_fops = { | |
307 | .owner = THIS_MODULE, | |
308 | .open = proc_vis_data_open, | |
309 | .read = seq_read, | |
310 | .write = proc_dummy_write, | |
5beef3c9 AL |
311 | .llseek = seq_lseek, |
312 | .release = single_release, | |
313 | }; | |
314 | ||
5beef3c9 AL |
315 | static const struct file_operations proc_interfaces_fops = { |
316 | .owner = THIS_MODULE, | |
317 | .open = proc_interfaces_open, | |
318 | .read = seq_read, | |
319 | .write = proc_interfaces_write, | |
320 | .llseek = seq_lseek, | |
321 | .release = single_release, | |
322 | }; | |
323 | ||
324 | static const struct file_operations proc_orig_interval_fops = { | |
325 | .owner = THIS_MODULE, | |
326 | .open = proc_orig_interval_open, | |
327 | .read = seq_read, | |
328 | .write = proc_orig_interval_write, | |
329 | .llseek = seq_lseek, | |
330 | .release = single_release, | |
331 | }; | |
332 | ||
333 | void cleanup_procfs(void) | |
334 | { | |
5beef3c9 AL |
335 | if (proc_orig_interval_file) |
336 | remove_proc_entry(PROC_FILE_ORIG_INTERVAL, proc_batman_dir); | |
337 | ||
338 | if (proc_interface_file) | |
339 | remove_proc_entry(PROC_FILE_INTERFACES, proc_batman_dir); | |
340 | ||
e9b76450 ML |
341 | if (proc_vis_data_file) |
342 | remove_proc_entry(PROC_FILE_VIS_DATA, proc_batman_dir); | |
343 | ||
344 | if (proc_vis_srv_file) | |
345 | remove_proc_entry(PROC_FILE_VIS_SRV, proc_batman_dir); | |
5beef3c9 | 346 | |
5beef3c9 AL |
347 | if (proc_batman_dir) |
348 | #ifdef __NET_NET_NAMESPACE_H | |
349 | remove_proc_entry(PROC_ROOT_DIR, init_net.proc_net); | |
350 | #else | |
351 | remove_proc_entry(PROC_ROOT_DIR, proc_net); | |
352 | #endif | |
353 | } | |
354 | ||
355 | int setup_procfs(void) | |
356 | { | |
357 | #ifdef __NET_NET_NAMESPACE_H | |
358 | proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, init_net.proc_net); | |
359 | #else | |
360 | proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, proc_net); | |
361 | #endif | |
362 | ||
363 | if (!proc_batman_dir) { | |
364 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s' folder failed\n", PROC_ROOT_DIR); | |
365 | return -EFAULT; | |
366 | } | |
367 | ||
368 | proc_interface_file = create_proc_entry(PROC_FILE_INTERFACES, | |
369 | S_IWUSR | S_IRUGO, | |
370 | proc_batman_dir); | |
371 | if (proc_interface_file) { | |
372 | proc_interface_file->proc_fops = &proc_interfaces_fops; | |
373 | } else { | |
374 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_INTERFACES); | |
375 | cleanup_procfs(); | |
376 | return -EFAULT; | |
377 | } | |
378 | ||
379 | proc_orig_interval_file = create_proc_entry(PROC_FILE_ORIG_INTERVAL, | |
380 | S_IWUSR | S_IRUGO, | |
381 | proc_batman_dir); | |
382 | if (proc_orig_interval_file) { | |
383 | proc_orig_interval_file->proc_fops = &proc_orig_interval_fops; | |
384 | } else { | |
385 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIG_INTERVAL); | |
386 | cleanup_procfs(); | |
387 | return -EFAULT; | |
388 | } | |
389 | ||
e9b76450 ML |
390 | proc_vis_srv_file = create_proc_entry(PROC_FILE_VIS_SRV, |
391 | S_IWUSR | S_IRUGO, | |
392 | proc_batman_dir); | |
393 | if (proc_vis_srv_file) { | |
394 | proc_vis_srv_file->proc_fops = &proc_vis_srv_fops; | |
395 | } else { | |
396 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_SRV); | |
397 | cleanup_procfs(); | |
398 | return -EFAULT; | |
399 | } | |
400 | ||
401 | proc_vis_data_file = create_proc_entry(PROC_FILE_VIS_DATA, S_IRUGO, | |
5beef3c9 | 402 | proc_batman_dir); |
e9b76450 ML |
403 | if (proc_vis_data_file) { |
404 | proc_vis_data_file->proc_fops = &proc_vis_data_fops; | |
5beef3c9 | 405 | } else { |
e9b76450 | 406 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_DATA); |
5beef3c9 AL |
407 | cleanup_procfs(); |
408 | return -EFAULT; | |
409 | } | |
410 | ||
5beef3c9 AL |
411 | return 0; |
412 | } |