Root/package/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
23static LIST_HEAD(trelay_devs);
24static struct dentry *debugfs_dir;
25
26struct trelay {
27    struct list_head list;
28    struct net_device *dev1, *dev2;
29    struct dentry *debugfs;
30    char name[];
31};
32
33rx_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
53static int trelay_open(struct inode *inode, struct file *file)
54{
55    file->private_data = inode->i_private;
56    return 0;
57}
58
59static 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
75static 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
86static 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
101out:
102    return NOTIFY_DONE;
103}
104
105static 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
121static 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
129static 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
176out:
177    rcu_read_unlock();
178    rtnl_unlock();
179    if (ret < 0)
180        kfree(tr);
181
182    return ret;
183}
184
185static 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
225static const struct file_operations fops_add = {
226    .owner = THIS_MODULE,
227    .write = trelay_add_write,
228    .llseek = default_llseek,
229};
230
231static struct notifier_block tr_dev_notifier = {
232    .notifier_call = tr_device_event
233};
234
235static 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
251error:
252    debugfs_remove_recursive(debugfs_dir);
253    return ret;
254}
255
256static 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
270module_init(trelay_init);
271module_exit(trelay_exit);
272MODULE_LICENSE("GPL");
273

Archive Download this file



interactive