2 * trelay.c: Trivial Ethernet Relay
4 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
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.
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.
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>
23 static LIST_HEAD(trelay_devs
);
24 static struct dentry
*debugfs_dir
;
27 struct list_head list
;
28 struct net_device
*dev1
, *dev2
;
29 struct dentry
*debugfs
;
33 rx_handler_result_t
trelay_handle_frame(struct sk_buff
**pskb
)
35 struct net_device
*dev
;
36 struct sk_buff
*skb
= *pskb
;
38 dev
= rcu_dereference(skb
->dev
->rx_handler_data
);
40 return RX_HANDLER_PASS
;
42 if (skb
->protocol
== htons(ETH_P_PAE
))
43 return RX_HANDLER_PASS
;
45 skb_push(skb
, ETH_HLEN
);
47 skb_forward_csum(skb
);
50 return RX_HANDLER_CONSUMED
;
53 static int trelay_open(struct inode
*inode
, struct file
*file
)
55 file
->private_data
= inode
->i_private
;
59 static int trelay_do_remove(struct trelay
*tr
)
66 netdev_rx_handler_unregister(tr
->dev1
);
67 netdev_rx_handler_unregister(tr
->dev2
);
69 debugfs_remove_recursive(tr
->debugfs
);
75 static struct trelay
*trelay_find(struct net_device
*dev
)
79 list_for_each_entry(tr
, &trelay_devs
, list
) {
80 if (tr
->dev1
== dev
|| tr
->dev2
== dev
)
86 static int tr_device_event(struct notifier_block
*unused
, unsigned long event
,
89 struct net_device
*dev
= ptr
;
92 if (event
!= NETDEV_UNREGISTER
)
95 tr
= trelay_find(dev
);
105 static ssize_t
trelay_remove_write(struct file
*file
, const char __user
*ubuf
,
106 size_t count
, loff_t
*ppos
)
108 struct trelay
*tr
= file
->private_data
;
112 ret
= trelay_do_remove(tr
);
121 static const struct file_operations fops_remove
= {
122 .owner
= THIS_MODULE
,
124 .write
= trelay_remove_write
,
125 .llseek
= default_llseek
,
129 static int trelay_do_add(char *name
, char *devn1
, char *devn2
)
131 struct net_device
*dev1
, *dev2
;
132 struct trelay
*tr
, *tr1
;
135 tr
= kzalloc(sizeof(*tr
) + strlen(name
) + 1, GFP_KERNEL
);
143 list_for_each_entry(tr1
, &trelay_devs
, list
) {
144 if (!strcmp(tr1
->name
, name
))
149 dev1
= dev_get_by_name_rcu(&init_net
, devn1
);
150 dev2
= dev_get_by_name_rcu(&init_net
, devn2
);
154 ret
= netdev_rx_handler_register(dev1
, trelay_handle_frame
, dev2
);
158 ret
= netdev_rx_handler_register(dev2
, trelay_handle_frame
, dev1
);
160 netdev_rx_handler_unregister(dev1
);
167 strcpy(tr
->name
, name
);
170 list_add_tail(&tr
->list
, &trelay_devs
);
172 tr
->debugfs
= debugfs_create_dir(name
, debugfs_dir
);
173 debugfs_create_file("remove", S_IWUSR
, tr
->debugfs
, tr
, &fops_remove
);
185 static ssize_t
trelay_add_write(struct file
*file
, const char __user
*ubuf
,
186 size_t count
, loff_t
*ppos
)
189 char *dev1
, *dev2
, *tmp
;
192 len
= min(count
, sizeof(buf
) - 1);
193 if (copy_from_user(buf
, ubuf
, len
))
198 if ((tmp
= strchr(buf
, '\n')))
201 dev1
= strchr(buf
, ',');
207 dev2
= strchr(dev1
, ',');
212 if (strchr(dev2
, ','))
215 if (!strlen(buf
) || !strlen(dev1
) || !strlen(dev2
))
218 ret
= trelay_do_add(buf
, dev1
, dev2
);
225 static const struct file_operations fops_add
= {
226 .owner
= THIS_MODULE
,
227 .write
= trelay_add_write
,
228 .llseek
= default_llseek
,
231 static struct notifier_block tr_dev_notifier
= {
232 .notifier_call
= tr_device_event
235 static int __init
trelay_init(void)
239 debugfs_dir
= debugfs_create_dir("trelay", NULL
);
243 debugfs_create_file("add", S_IWUSR
, debugfs_dir
, NULL
, &fops_add
);
245 ret
= register_netdevice_notifier(&tr_dev_notifier
);
252 debugfs_remove_recursive(debugfs_dir
);
256 static void __exit
trelay_exit(void)
258 struct trelay
*tr
, *tmp
;
260 unregister_netdevice_notifier(&tr_dev_notifier
);
263 list_for_each_entry_safe(tr
, tmp
, &trelay_devs
, list
)
264 trelay_do_remove(tr
);
267 debugfs_remove_recursive(debugfs_dir
);
270 module_init(trelay_init
);
271 module_exit(trelay_exit
);
272 MODULE_LICENSE("GPL");