packages: clean up the package folder
[openwrt/staging/wigyori.git] / package / kernel / trelay / src / trelay.c
1 /*
2 * trelay.c: Trivial Ethernet Relay
3 *
4 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16 #include <linux/module.h>
17 #include <linux/list.h>
18 #include <linux/mutex.h>
19 #include <linux/netdevice.h>
20 #include <linux/rtnetlink.h>
21 #include <linux/debugfs.h>
22
23 static LIST_HEAD(trelay_devs);
24 static struct dentry *debugfs_dir;
25
26 struct trelay {
27 struct list_head list;
28 struct net_device *dev1, *dev2;
29 struct dentry *debugfs;
30 char name[];
31 };
32
33 rx_handler_result_t trelay_handle_frame(struct sk_buff **pskb)
34 {
35 struct net_device *dev;
36 struct sk_buff *skb = *pskb;
37
38 dev = rcu_dereference(skb->dev->rx_handler_data);
39 if (!dev)
40 return RX_HANDLER_PASS;
41
42 if (skb->protocol == htons(ETH_P_PAE))
43 return RX_HANDLER_PASS;
44
45 skb_push(skb, ETH_HLEN);
46 skb->dev = dev;
47 skb_forward_csum(skb);
48 dev_queue_xmit(skb);
49
50 return RX_HANDLER_CONSUMED;
51 }
52
53 static int trelay_open(struct inode *inode, struct file *file)
54 {
55 file->private_data = inode->i_private;
56 return 0;
57 }
58
59 static int trelay_do_remove(struct trelay *tr)
60 {
61 list_del(&tr->list);
62
63 dev_put(tr->dev1);
64 dev_put(tr->dev2);
65
66 netdev_rx_handler_unregister(tr->dev1);
67 netdev_rx_handler_unregister(tr->dev2);
68
69 debugfs_remove_recursive(tr->debugfs);
70 kfree(tr);
71
72 return 0;
73 }
74
75 static struct trelay *trelay_find(struct net_device *dev)
76 {
77 struct trelay *tr;
78
79 list_for_each_entry(tr, &trelay_devs, list) {
80 if (tr->dev1 == dev || tr->dev2 == dev)
81 return tr;
82 }
83 return NULL;
84 }
85
86 static int tr_device_event(struct notifier_block *unused, unsigned long event,
87 void *ptr)
88 {
89 struct net_device *dev = ptr;
90 struct trelay *tr;
91
92 if (event != NETDEV_UNREGISTER)
93 goto out;
94
95 tr = trelay_find(dev);
96 if (!tr)
97 goto out;
98
99 trelay_do_remove(tr);
100
101 out:
102 return NOTIFY_DONE;
103 }
104
105 static ssize_t trelay_remove_write(struct file *file, const char __user *ubuf,
106 size_t count, loff_t *ppos)
107 {
108 struct trelay *tr = file->private_data;
109 int ret;
110
111 rtnl_lock();
112 ret = trelay_do_remove(tr);
113 rtnl_unlock();
114
115 if (ret < 0)
116 return ret;
117
118 return count;
119 }
120
121 static const struct file_operations fops_remove = {
122 .owner = THIS_MODULE,
123 .open = trelay_open,
124 .write = trelay_remove_write,
125 .llseek = default_llseek,
126 };
127
128
129 static int trelay_do_add(char *name, char *devn1, char *devn2)
130 {
131 struct net_device *dev1, *dev2;
132 struct trelay *tr, *tr1;
133 int ret;
134
135 tr = kzalloc(sizeof(*tr) + strlen(name) + 1, GFP_KERNEL);
136 if (!tr)
137 return -ENOMEM;
138
139 rtnl_lock();
140 rcu_read_lock();
141
142 ret = -EEXIST;
143 list_for_each_entry(tr1, &trelay_devs, list) {
144 if (!strcmp(tr1->name, name))
145 goto out;
146 }
147
148 ret = -ENOENT;
149 dev1 = dev_get_by_name_rcu(&init_net, devn1);
150 dev2 = dev_get_by_name_rcu(&init_net, devn2);
151 if (!dev1 || !dev2)
152 goto out;
153
154 ret = netdev_rx_handler_register(dev1, trelay_handle_frame, dev2);
155 if (ret < 0)
156 goto out;
157
158 ret = netdev_rx_handler_register(dev2, trelay_handle_frame, dev1);
159 if (ret < 0) {
160 netdev_rx_handler_unregister(dev1);
161 goto out;
162 }
163
164 dev_hold(dev1);
165 dev_hold(dev2);
166
167 strcpy(tr->name, name);
168 tr->dev1 = dev1;
169 tr->dev2 = dev2;
170 list_add_tail(&tr->list, &trelay_devs);
171
172 tr->debugfs = debugfs_create_dir(name, debugfs_dir);
173 debugfs_create_file("remove", S_IWUSR, tr->debugfs, tr, &fops_remove);
174 ret = 0;
175
176 out:
177 rcu_read_unlock();
178 rtnl_unlock();
179 if (ret < 0)
180 kfree(tr);
181
182 return ret;
183 }
184
185 static ssize_t trelay_add_write(struct file *file, const char __user *ubuf,
186 size_t count, loff_t *ppos)
187 {
188 char buf[256];
189 char *dev1, *dev2, *tmp;
190 ssize_t len, ret;
191
192 len = min(count, sizeof(buf) - 1);
193 if (copy_from_user(buf, ubuf, len))
194 return -EFAULT;
195
196 buf[len] = 0;
197
198 if ((tmp = strchr(buf, '\n')))
199 *tmp = 0;
200
201 dev1 = strchr(buf, ',');
202 if (!dev1)
203 return -EINVAL;
204
205 *(dev1++) = 0;
206
207 dev2 = strchr(dev1, ',');
208 if (!dev2)
209 return -EINVAL;
210
211 *(dev2++) = 0;
212 if (strchr(dev2, ','))
213 return -EINVAL;
214
215 if (!strlen(buf) || !strlen(dev1) || !strlen(dev2))
216 return -EINVAL;
217
218 ret = trelay_do_add(buf, dev1, dev2);
219 if (ret < 0)
220 return ret;
221
222 return count;
223 }
224
225 static const struct file_operations fops_add = {
226 .owner = THIS_MODULE,
227 .write = trelay_add_write,
228 .llseek = default_llseek,
229 };
230
231 static struct notifier_block tr_dev_notifier = {
232 .notifier_call = tr_device_event
233 };
234
235 static int __init trelay_init(void)
236 {
237 int ret;
238
239 debugfs_dir = debugfs_create_dir("trelay", NULL);
240 if (!debugfs_dir)
241 return -ENOMEM;
242
243 debugfs_create_file("add", S_IWUSR, debugfs_dir, NULL, &fops_add);
244
245 ret = register_netdevice_notifier(&tr_dev_notifier);
246 if (ret < 0)
247 goto error;
248
249 return 0;
250
251 error:
252 debugfs_remove_recursive(debugfs_dir);
253 return ret;
254 }
255
256 static void __exit trelay_exit(void)
257 {
258 struct trelay *tr, *tmp;
259
260 unregister_netdevice_notifier(&tr_dev_notifier);
261
262 rtnl_lock();
263 list_for_each_entry_safe(tr, tmp, &trelay_devs, list)
264 trelay_do_remove(tr);
265 rtnl_unlock();
266
267 debugfs_remove_recursive(debugfs_dir);
268 }
269
270 module_init(trelay_init);
271 module_exit(trelay_exit);
272 MODULE_LICENSE("GPL");