2 * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
4 * Marek Lindner, Simon Wunderlich
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.
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.
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
25 #include "translation-table.h"
26 #include "hard-interface.h"
32 static uint8_t vis_format = DOT_DRAW;
34 static struct proc_dir_entry *proc_batman_dir, *proc_interface_file;
35 static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file;
36 static struct proc_dir_entry *proc_transt_local_file;
37 static struct proc_dir_entry *proc_transt_global_file;
38 static struct proc_dir_entry *proc_vis_file, *proc_vis_format_file;
39 static struct proc_dir_entry *proc_aggr_file;
41 static int proc_interfaces_read(struct seq_file *seq, void *offset)
43 struct batman_if *batman_if;
46 list_for_each_entry_rcu(batman_if, &if_list, list) {
47 seq_printf(seq, "[%8s] %s %s \n",
48 (batman_if->if_active == IF_ACTIVE ?
49 "active" : "inactive"),
51 (batman_if->if_active == IF_ACTIVE ?
52 batman_if->addr_str : " "));
59 static int proc_interfaces_open(struct inode *inode, struct file *file)
61 return single_open(file, proc_interfaces_read, NULL);
64 static ssize_t proc_interfaces_write(struct file *instance,
65 const char __user *userbuffer,
66 size_t count, loff_t *data)
68 char *if_string, *colon_ptr = NULL, *cr_ptr = NULL;
69 int not_copied = 0, if_num = 0;
70 struct batman_if *batman_if = NULL;
72 if_string = kmalloc(count, GFP_KERNEL);
77 if (count > IFNAMSIZ - 1) {
78 printk(KERN_WARNING "batman-adv:Can't add interface: device name is too long\n");
82 not_copied = copy_from_user(if_string, userbuffer, count);
83 if_string[count - not_copied - 1] = 0;
85 colon_ptr = strchr(if_string, ':');
90 cr_ptr = strchr(if_string, '\n');
95 if (strlen(if_string) == 0) {
103 list_for_each_entry_rcu(batman_if, &if_list, list) {
104 if (strncmp(batman_if->dev, if_string, count) == 0) {
105 printk(KERN_ERR "batman-adv:Given interface is already active: %s\n", if_string);
115 hardif_add_interface(if_string, if_num);
117 if ((atomic_read(&module_state) == MODULE_INACTIVE) &&
118 (hardif_get_active_if_num() > 0))
122 if (list_empty(&if_list)) {
128 num_ifs = if_num + 1;
136 static int proc_orig_interval_read(struct seq_file *seq, void *offset)
138 seq_printf(seq, "%i\n", atomic_read(&originator_interval));
143 static ssize_t proc_orig_interval_write(struct file *file,
144 const char __user *buffer,
145 size_t count, loff_t *ppos)
147 char *interval_string;
149 unsigned long originator_interval_tmp;
152 interval_string = kmalloc(count, GFP_KERNEL);
154 if (!interval_string)
157 not_copied = copy_from_user(interval_string, buffer, count);
158 interval_string[count - not_copied - 1] = 0;
160 retval = strict_strtoul(interval_string, 10, &originator_interval_tmp);
162 printk(KERN_ERR "batman-adv:New originator interval invalid\n");
166 if (originator_interval_tmp <= JITTER * 2) {
167 printk(KERN_WARNING "batman-adv:New originator interval too small: %li (min: %i)\n",
168 originator_interval_tmp, JITTER * 2);
172 printk(KERN_INFO "batman-adv:Changing originator interval from: %i to: %li\n",
173 atomic_read(&originator_interval), originator_interval_tmp);
175 atomic_set(&originator_interval, originator_interval_tmp);
178 kfree(interval_string);
182 static int proc_orig_interval_open(struct inode *inode, struct file *file)
184 return single_open(file, proc_orig_interval_read, NULL);
187 static int proc_originators_read(struct seq_file *seq, void *offset)
189 struct hash_it_t *hashit = NULL;
190 struct orig_node *orig_node;
191 struct neigh_node *neigh_node;
192 int batman_count = 0;
193 char orig_str[ETH_STR_LEN], router_str[ETH_STR_LEN];
196 if (list_empty(&if_list)) {
198 seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n");
202 if (((struct batman_if *)if_list.next)->if_active != IF_ACTIVE) {
204 seq_printf(seq, "BATMAN disabled - primary interface not active \n");
209 " %-14s (%s/%i) %17s [%10s]: %20s ... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s] \n",
210 "Originator", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF",
211 "Potential nexthops", SOURCE_VERSION, REVISION_VERSION_STR,
212 ((struct batman_if *)if_list.next)->dev,
213 ((struct batman_if *)if_list.next)->addr_str);
216 spin_lock(&orig_hash_lock);
218 while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
220 orig_node = hashit->bucket->data;
222 if (!orig_node->router)
225 if (orig_node->router->tq_avg == 0)
230 addr_to_string(orig_str, orig_node->orig);
231 addr_to_string(router_str, orig_node->router->addr);
233 seq_printf(seq, "%-17s (%3i) %17s [%10s]:",
234 orig_str, orig_node->router->tq_avg,
235 router_str, orig_node->router->if_incoming->dev);
237 list_for_each_entry(neigh_node, &orig_node->neigh_list, list) {
238 addr_to_string(orig_str, neigh_node->addr);
239 seq_printf(seq, " %17s (%3i)",
240 orig_str, neigh_node->tq_avg);
243 seq_printf(seq, "\n");
247 spin_unlock(&orig_hash_lock);
249 if (batman_count == 0)
250 seq_printf(seq, "No batman nodes in range ... \n");
256 static int proc_originators_open(struct inode *inode, struct file *file)
258 return single_open(file, proc_originators_read, NULL);
261 static int proc_transt_local_read(struct seq_file *seq, void *offset)
265 buf = kmalloc(4096, GFP_KERNEL);
270 if (list_empty(&if_list)) {
272 seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n");
278 seq_printf(seq, "Locally retrieved addresses (from %s) announced via HNA:\n", soft_device->name);
280 hna_local_fill_buffer_text(buf, 4096);
281 seq_printf(seq, "%s", buf);
288 static int proc_transt_local_open(struct inode *inode, struct file *file)
290 return single_open(file, proc_transt_local_read, NULL);
293 static int proc_transt_global_read(struct seq_file *seq, void *offset)
297 buf = kmalloc(4096, GFP_KERNEL);
302 if (list_empty(&if_list)) {
304 seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n");
310 seq_printf(seq, "Globally announced HNAs received via the mesh (translation table):\n");
312 hna_global_fill_buffer_text(buf, 4096);
313 seq_printf(seq, "%s", buf);
320 static int proc_transt_global_open(struct inode *inode, struct file *file)
322 return single_open(file, proc_transt_global_read, NULL);
325 /* insert interface to the list of interfaces of one originator */
327 static void proc_vis_insert_interface(const uint8_t *interface,
328 struct vis_if_list **if_entry,
331 /* Did we get an empty list? (then insert imediately) */
332 if (*if_entry == NULL) {
333 *if_entry = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL);
334 if (*if_entry == NULL)
337 (*if_entry)->primary = primary;
338 (*if_entry)->next = NULL;
339 memcpy((*if_entry)->addr, interface, ETH_ALEN);
341 struct vis_if_list *head_if_entry = *if_entry;
342 /* Do we already have this interface in our list? */
343 while (!compare_orig((*if_entry)->addr, (void *)interface)) {
345 /* Or did we reach the end (then append the interface) */
346 if ((*if_entry)->next == NULL) {
347 (*if_entry)->next = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL);
348 if ((*if_entry)->next == NULL)
351 memcpy((*if_entry)->next->addr, interface, ETH_ALEN);
352 (*if_entry)->next->primary = primary;
353 (*if_entry)->next->next = NULL;
356 *if_entry = (*if_entry)->next;
358 /* Rewind the list to its head */
359 *if_entry = head_if_entry;
364 static void proc_vis_read_entry(struct seq_file *seq,
365 struct vis_info_entry *entry,
366 struct vis_if_list **if_entry,
368 uint8_t current_format,
373 int int_part, frac_part;
375 addr_to_string(to, entry->dest);
376 if (entry->quality == 0) {
377 #ifndef VIS_SUBCLUSTERS_DISABLED
378 proc_vis_insert_interface(vis_orig, if_entry, true);
379 #endif /* VIS_SUBCLUSTERS_DISABLED */
380 addr_to_string(from, vis_orig);
381 if (current_format == DOT_DRAW) {
382 seq_printf(seq, "\t\"%s\" -> \"%s\" [label=\"HNA\"]\n",
386 "%s\t{ router : \"%s\", gateway : \"%s\", label : \"HNA\" }",
387 (first_line ? "" : ",\n"), from, to);
390 #ifndef VIS_SUBCLUSTERS_DISABLED
391 proc_vis_insert_interface(entry->src, if_entry, compare_orig(entry->src, vis_orig));
392 #endif /* VIS_SUBCLUSTERS_DISABLED */
393 addr_to_string(from, entry->src);
395 /* kernel has no printf-support for %f? it'd be better to return
398 int_part = TQ_MAX_VALUE / entry->quality;
399 frac_part = 1000 * TQ_MAX_VALUE / entry->quality - int_part * 1000;
401 if (current_format == DOT_DRAW) {
403 "\t\"%s\" -> \"%s\" [label=\"%d.%d\"]\n",
404 from, to, int_part, frac_part);
407 "%s\t{ router : \"%s\", neighbour : \"%s\", label : %d.%d }",
408 (first_line ? "" : ",\n"), from, to, int_part, frac_part);
414 static int proc_vis_read(struct seq_file *seq, void *offset)
416 struct hash_it_t *hashit = NULL;
417 struct vis_info *info;
418 struct vis_info_entry *entries;
419 struct vis_if_list *if_entries = NULL;
421 uint8_t current_format, first_line = 1;
422 #ifndef VIS_SUBCLUSTERS_DISABLED
423 char tmp_addr_str[ETH_STR_LEN];
424 struct vis_if_list *tmp_if_next;
425 #endif /* VIS_SUBCLUSTERS_DISABLED */
427 current_format = vis_format;
430 if (list_empty(&if_list) || (!is_vis_server())) {
432 if (current_format == DOT_DRAW)
433 seq_printf(seq, "digraph {\n}\n");
439 if (current_format == DOT_DRAW)
440 seq_printf(seq, "digraph {\n");
442 spin_lock(&vis_hash_lock);
443 while (NULL != (hashit = hash_iterate(vis_hash, hashit))) {
444 info = hashit->bucket->data;
445 entries = (struct vis_info_entry *)
446 ((char *)info + sizeof(struct vis_info));
448 for (i = 0; i < info->packet.entries; i++) {
449 proc_vis_read_entry(seq, &entries[i], &if_entries,
450 info->packet.vis_orig,
451 current_format, first_line);
456 #ifndef VIS_SUBCLUSTERS_DISABLED
457 /* Generate subgraphs from the collected items */
458 if (current_format == DOT_DRAW) {
460 addr_to_string(tmp_addr_str, info->packet.vis_orig);
461 seq_printf(seq, "\tsubgraph \"cluster_%s\" {\n", tmp_addr_str);
462 while (if_entries != NULL) {
464 addr_to_string(tmp_addr_str, if_entries->addr);
465 if (if_entries->primary)
466 seq_printf(seq, "\t\t\"%s\" [peripheries=2]\n", tmp_addr_str);
468 seq_printf(seq, "\t\t\"%s\"\n", tmp_addr_str);
470 /* ... and empty the list while doing this */
471 tmp_if_next = if_entries->next;
473 if_entries = tmp_if_next;
475 seq_printf(seq, "\t}\n");
477 #endif /* VIS_SUBCLUSTERS_DISABLED */
479 spin_unlock(&vis_hash_lock);
481 if (current_format == DOT_DRAW)
482 seq_printf(seq, "}\n");
484 seq_printf(seq, "\n");
489 /* setting the mode of the vis server by the user */
490 static ssize_t proc_vis_write(struct file *file, const char __user * buffer,
491 size_t count, loff_t *ppos)
493 char *vis_mode_string;
496 vis_mode_string = kmalloc(count, GFP_KERNEL);
498 if (!vis_mode_string)
501 not_copied = copy_from_user(vis_mode_string, buffer, count);
502 vis_mode_string[count - not_copied - 1] = 0;
504 if (strcmp(vis_mode_string, "client") == 0) {
505 printk(KERN_INFO "batman-adv:Setting VIS mode to client\n");
506 vis_set_mode(VIS_TYPE_CLIENT_UPDATE);
507 } else if (strcmp(vis_mode_string, "server") == 0) {
508 printk(KERN_INFO "batman-adv:Setting VIS mode to server\n");
509 vis_set_mode(VIS_TYPE_SERVER_SYNC);
511 printk(KERN_ERR "batman-adv:Unknown VIS mode: %s\n",
514 kfree(vis_mode_string);
518 static int proc_vis_open(struct inode *inode, struct file *file)
520 return single_open(file, proc_vis_read, NULL);
523 static int proc_vis_format_read(struct seq_file *seq, void *offset)
525 uint8_t current_format = vis_format;
527 seq_printf(seq, "[%c] %s\n",
528 (current_format == DOT_DRAW) ? 'x' : ' ',
530 seq_printf(seq, "[%c] %s\n",
531 (current_format == JSON) ? 'x' : ' ',
532 VIS_FORMAT_JSON_NAME);
536 static int proc_vis_format_open(struct inode *inode, struct file *file)
538 return single_open(file, proc_vis_format_read, NULL);
541 static ssize_t proc_vis_format_write(struct file *file,
542 const char __user *buffer,
543 size_t count, loff_t *ppos)
545 char *vis_format_string;
548 vis_format_string = kmalloc(count, GFP_KERNEL);
550 if (!vis_format_string)
553 not_copied = copy_from_user(vis_format_string, buffer, count);
554 vis_format_string[count - not_copied - 1] = 0;
556 if (strcmp(vis_format_string, VIS_FORMAT_DD_NAME) == 0) {
557 printk(KERN_INFO "batman-adv:Setting VIS output format to: %s\n",
559 vis_format = DOT_DRAW;
560 } else if (strcmp(vis_format_string, VIS_FORMAT_JSON_NAME) == 0) {
561 printk(KERN_INFO "batman-adv:Setting VIS output format to: %s\n",
562 VIS_FORMAT_JSON_NAME);
565 printk(KERN_ERR "batman-adv:Unknown VIS output format: %s\n",
568 kfree(vis_format_string);
572 static int proc_aggr_read(struct seq_file *seq, void *offset)
574 seq_printf(seq, "%i\n", atomic_read(&aggregation_enabled));
579 static ssize_t proc_aggr_write(struct file *file, const char __user *buffer,
580 size_t count, loff_t *ppos)
584 unsigned long aggregation_enabled_tmp;
586 aggr_string = kmalloc(count, GFP_KERNEL);
591 not_copied = copy_from_user(aggr_string, buffer, count);
592 aggr_string[count - not_copied - 1] = 0;
594 strict_strtoul(aggr_string, 10, &aggregation_enabled_tmp);
596 if ((aggregation_enabled_tmp != 0) && (aggregation_enabled_tmp != 1)) {
597 printk(KERN_ERR "batman-adv:Aggregation can only be enabled (1) or disabled (0), given value: %li\n", aggregation_enabled_tmp);
601 printk(KERN_INFO "batman-adv:Changing aggregation from: %s (%i) to: %s (%li)\n",
602 (atomic_read(&aggregation_enabled) == 1 ?
603 "enabled" : "disabled"),
604 atomic_read(&aggregation_enabled),
605 (aggregation_enabled_tmp == 1 ? "enabled" : "disabled"),
606 aggregation_enabled_tmp);
608 atomic_set(&aggregation_enabled, (unsigned)aggregation_enabled_tmp);
614 static int proc_aggr_open(struct inode *inode, struct file *file)
616 return single_open(file, proc_aggr_read, NULL);
619 /* satisfying different prototypes ... */
620 static ssize_t proc_dummy_write(struct file *file, const char __user *buffer,
621 size_t count, loff_t *ppos)
626 static const struct file_operations proc_aggr_fops = {
627 .owner = THIS_MODULE,
628 .open = proc_aggr_open,
630 .write = proc_aggr_write,
632 .release = single_release,
635 static const struct file_operations proc_vis_format_fops = {
636 .owner = THIS_MODULE,
637 .open = proc_vis_format_open,
639 .write = proc_vis_format_write,
641 .release = single_release,
644 static const struct file_operations proc_vis_fops = {
645 .owner = THIS_MODULE,
646 .open = proc_vis_open,
648 .write = proc_vis_write,
650 .release = single_release,
653 static const struct file_operations proc_originators_fops = {
654 .owner = THIS_MODULE,
655 .open = proc_originators_open,
657 .write = proc_dummy_write,
659 .release = single_release,
662 static const struct file_operations proc_transt_local_fops = {
663 .owner = THIS_MODULE,
664 .open = proc_transt_local_open,
666 .write = proc_dummy_write,
668 .release = single_release,
671 static const struct file_operations proc_transt_global_fops = {
672 .owner = THIS_MODULE,
673 .open = proc_transt_global_open,
675 .write = proc_dummy_write,
677 .release = single_release,
680 static const struct file_operations proc_interfaces_fops = {
681 .owner = THIS_MODULE,
682 .open = proc_interfaces_open,
684 .write = proc_interfaces_write,
686 .release = single_release,
689 static const struct file_operations proc_orig_interval_fops = {
690 .owner = THIS_MODULE,
691 .open = proc_orig_interval_open,
693 .write = proc_orig_interval_write,
695 .release = single_release,
698 void cleanup_procfs(void)
700 if (proc_transt_global_file)
701 remove_proc_entry(PROC_FILE_TRANST_GLOBAL, proc_batman_dir);
703 if (proc_transt_local_file)
704 remove_proc_entry(PROC_FILE_TRANST_LOCAL, proc_batman_dir);
706 if (proc_originators_file)
707 remove_proc_entry(PROC_FILE_ORIGINATORS, proc_batman_dir);
709 if (proc_orig_interval_file)
710 remove_proc_entry(PROC_FILE_ORIG_INTERVAL, proc_batman_dir);
712 if (proc_interface_file)
713 remove_proc_entry(PROC_FILE_INTERFACES, proc_batman_dir);
716 remove_proc_entry(PROC_FILE_VIS, proc_batman_dir);
718 if (proc_vis_format_file)
719 remove_proc_entry(PROC_FILE_VIS_FORMAT, proc_batman_dir);
722 remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir);
725 #ifdef __NET_NET_NAMESPACE_H
726 remove_proc_entry(PROC_ROOT_DIR, init_net.proc_net);
728 remove_proc_entry(PROC_ROOT_DIR, proc_net);
732 int setup_procfs(void)
734 #ifdef __NET_NET_NAMESPACE_H
735 proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, init_net.proc_net);
737 proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, proc_net);
740 if (!proc_batman_dir) {
741 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s' folder failed\n", PROC_ROOT_DIR);
745 proc_interface_file = create_proc_entry(PROC_FILE_INTERFACES,
748 if (proc_interface_file) {
749 proc_interface_file->proc_fops = &proc_interfaces_fops;
751 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_INTERFACES);
756 proc_orig_interval_file = create_proc_entry(PROC_FILE_ORIG_INTERVAL,
759 if (proc_orig_interval_file) {
760 proc_orig_interval_file->proc_fops = &proc_orig_interval_fops;
762 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIG_INTERVAL);
767 proc_originators_file = create_proc_entry(PROC_FILE_ORIGINATORS,
768 S_IRUGO, proc_batman_dir);
769 if (proc_originators_file) {
770 proc_originators_file->proc_fops = &proc_originators_fops;
772 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIGINATORS);
777 proc_transt_local_file = create_proc_entry(PROC_FILE_TRANST_LOCAL,
778 S_IRUGO, proc_batman_dir);
779 if (proc_transt_local_file) {
780 proc_transt_local_file->proc_fops = &proc_transt_local_fops;
782 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_LOCAL);
787 proc_transt_global_file = create_proc_entry(PROC_FILE_TRANST_GLOBAL,
788 S_IRUGO, proc_batman_dir);
789 if (proc_transt_global_file) {
790 proc_transt_global_file->proc_fops = &proc_transt_global_fops;
792 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_GLOBAL);
797 proc_vis_file = create_proc_entry(PROC_FILE_VIS, S_IWUSR | S_IRUGO,
800 proc_vis_file->proc_fops = &proc_vis_fops;
802 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS);
807 proc_vis_format_file = create_proc_entry(PROC_FILE_VIS_FORMAT,
810 if (proc_vis_format_file) {
811 proc_vis_format_file->proc_fops = &proc_vis_format_fops;
813 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_FORMAT);
818 proc_aggr_file = create_proc_entry(PROC_FILE_AGGR, S_IWUSR | S_IRUGO,
820 if (proc_aggr_file) {
821 proc_aggr_file->proc_fops = &proc_aggr_fops;
823 printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_AGGR);