733e9a94d88b939f8708c6bd898be014ab1b286f
[cascardo/ovs.git] / datapath / brc_procfs.c
1 #include <linux/kernel.h>
2 #include <linux/module.h>
3 #include <linux/netdevice.h>
4 #include <linux/proc_fs.h>
5 #include <linux/seq_file.h>
6 #include <net/genetlink.h>
7 #include "openvswitch/brcompat-netlink.h"
8
9 /* This code implements a Generic Netlink command BRC_GENL_C_SET_PROC that can
10  * be used to add, modify, and delete arbitrary files in selected
11  * subdirectories of /proc.  It's a horrible kluge prompted by the need to
12  * simulate certain /proc/net/vlan and /proc/net/bonding files for software
13  * that wants to read them, and with any luck it will go away eventually.
14  *
15  * The implementation is a kluge too.  In particular, we want to release the
16  * strings copied into the 'data' members of proc_dir_entry when the
17  * proc_dir_entry structures are freed, but there doesn't appear to be a way to
18  * hook that, so instead we have to rely on being the only entity modifying the
19  * directories in question.
20  */
21
22 static int brc_seq_show(struct seq_file *seq, void *unused)
23 {
24         seq_puts(seq, seq->private);
25         return 0;
26 }
27
28 static int brc_seq_open(struct inode *inode, struct file *file)
29 {
30         return single_open(file, brc_seq_show, PDE(inode)->data);
31 }
32
33 static struct file_operations brc_fops = {
34         .owner = THIS_MODULE,
35         .open    = brc_seq_open,
36         .read    = seq_read,
37         .llseek  = seq_lseek,
38         .release = single_release,
39 };
40
41 static struct proc_dir_entry *proc_vlan_dir;
42 static struct proc_dir_entry *proc_bonding_dir;
43
44 struct proc_dir_entry *brc_lookup_entry(struct proc_dir_entry *de, const char *name)
45 {
46         int namelen = strlen(name);
47         for (de = de->subdir; de; de = de->next) {
48                 if (de->namelen != namelen)
49                         continue;
50                 if (!memcmp(name, de->name, de->namelen))
51                         return de;
52         }
53         return NULL;
54 }
55
56 static struct proc_dir_entry *brc_open_dir(const char *dir_name,
57                                            struct proc_dir_entry *parent,
58                                            struct proc_dir_entry **dirp)
59 {
60         if (!*dirp) {
61                 struct proc_dir_entry *dir;
62                 if (brc_lookup_entry(parent, dir_name)) {
63                         printk(KERN_WARNING "%s proc directory exists, can't "
64                                "simulate--probably its real module is "
65                                "loaded\n", dir_name);
66                         return NULL;
67                 }
68                 dir = *dirp = proc_mkdir(dir_name, parent);
69         }
70         return *dirp;
71 }
72
73 /* Maximum length of the BRC_GENL_A_PROC_DIR and BRC_GENL_A_PROC_NAME strings.
74  * If we could depend on supporting NLA_NUL_STRING and the .len member in
75  * Generic Netlink policy, then we could just put this in brc_genl_policy (and
76  * simplify brc_genl_set_proc() below too), but upstream 2.6.18 does not have
77  * either. */
78 #define BRC_NAME_LEN_MAX 32
79
80 int brc_genl_set_proc(struct sk_buff *skb, struct genl_info *info)
81 {
82         struct proc_dir_entry *dir, *entry;
83         const char *dir_name, *name;
84         char *data;
85
86         if (!info->attrs[BRC_GENL_A_PROC_DIR] ||
87             VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DIR]) ||
88             !info->attrs[BRC_GENL_A_PROC_NAME] ||
89             VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_NAME]) ||
90             (info->attrs[BRC_GENL_A_PROC_DATA] &&
91              VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DATA])))
92                 return -EINVAL;
93
94         dir_name = nla_data(info->attrs[BRC_GENL_A_PROC_DIR]);
95         name = nla_data(info->attrs[BRC_GENL_A_PROC_NAME]);
96         if (strlen(dir_name) > BRC_NAME_LEN_MAX ||
97             strlen(name) > BRC_NAME_LEN_MAX)
98                 return -EINVAL;
99
100         if (!strcmp(dir_name, "net/vlan"))
101                 dir = brc_open_dir("vlan", proc_net, &proc_vlan_dir);
102         else if (!strcmp(dir_name, "net/bonding"))
103                 dir = brc_open_dir("bonding", proc_net, &proc_bonding_dir);
104         else
105                 return -EINVAL;
106         if (!dir) {
107                 /* Probably failed because the module that really implements
108                  * the function in question is loaded and already owns the
109                  * directory in question.*/
110                 return -EBUSY;
111         }
112
113         entry = brc_lookup_entry(dir, name);
114         if (!info->attrs[BRC_GENL_A_PROC_DATA]) {
115                 if (!entry)
116                         return -ENOENT;
117
118                 data = entry->data;
119                 remove_proc_entry(name, dir);
120                 if (brc_lookup_entry(dir, name))
121                         return -EBUSY; /* Shouldn't happen */
122
123                 kfree(data);
124         } else {
125                 data = kstrdup(nla_data(info->attrs[BRC_GENL_A_PROC_DATA]),
126                                GFP_KERNEL);
127                 if (!data)
128                         return -ENOMEM;
129
130                 if (entry) {
131                         char *old_data = entry->data;
132                         entry->data = data;
133                         kfree(old_data);
134                         return 0;
135                 }
136
137                 entry = create_proc_entry(name, S_IFREG|S_IRUSR|S_IWUSR, dir);
138                 if (!entry) {
139                         kfree(data);
140                         return -ENOBUFS;
141                 }
142                 entry->proc_fops = &brc_fops;
143                 entry->data = data;
144         }
145         return 0;
146 }
147
148 static void kill_proc_dir(const char *dir_name,
149                           struct proc_dir_entry *parent,
150                           struct proc_dir_entry *dir)
151 {
152         if (!dir)
153                 return;
154         for (;;) {
155                 struct proc_dir_entry *e;
156                 char *data;
157                 char name[BRC_NAME_LEN_MAX + 1];
158
159                 e = dir->subdir;
160                 if (!e)
161                         break;
162
163                 if (e->namelen >= sizeof name) {
164                         /* Can't happen: we prevent adding names this long by
165                          * limiting the BRC_GENL_A_PROC_NAME string to
166                          * BRC_NAME_LEN_MAX bytes.  */
167                         WARN_ON(1);
168                         break;
169                 }
170                 strcpy(name, e->name);
171
172                 data = e->data;
173                 e->data = NULL;
174                 kfree(data);
175
176                 remove_proc_entry(name, dir);
177         }
178         remove_proc_entry(dir_name, parent);
179 }
180
181 void brc_procfs_exit(void)
182 {
183         kill_proc_dir("vlan", proc_net, proc_vlan_dir);
184         kill_proc_dir("bonding", proc_net, proc_bonding_dir);
185 }