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