2 * trelay.c: Trivial Ethernet Relay
4 * Copyright (C) 2012 Felix Fietkau <nbd@nbd.name>
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
;
34 rx_handler_result_t
trelay_handle_frame(struct sk_buff
**pskb
)
36 struct net_device
*dev
;
37 struct sk_buff
*skb
= *pskb
;
39 dev
= rcu_dereference(skb
->dev
->rx_handler_data
);
41 return RX_HANDLER_PASS
;
43 if (skb
->protocol
== htons(ETH_P_PAE
))
44 return RX_HANDLER_PASS
;
46 skb_push(skb
, ETH_HLEN
);
48 skb_forward_csum(skb
);
51 return RX_HANDLER_CONSUMED
;
54 static int trelay_open(struct inode
*inode
, struct file
*file
)
56 file
->private_data
= inode
->i_private
;
60 static int trelay_do_remove(struct trelay
*tr
)
64 /* First and before all, ensure that the debugfs file is removed
65 * to prevent dangling pointer in file->private_data */
66 debugfs_remove_recursive(tr
->debugfs
);
71 netdev_rx_handler_unregister(tr
->dev1
);
72 netdev_rx_handler_unregister(tr
->dev2
);
79 static struct trelay
*trelay_find(struct net_device
*dev
)
83 list_for_each_entry(tr
, &trelay_devs
, list
) {
84 if (tr
->dev1
== dev
|| tr
->dev2
== dev
)
90 static int tr_device_event(struct notifier_block
*unused
, unsigned long event
,
93 struct net_device
*dev
= netdev_notifier_info_to_dev(ptr
);
96 if (event
!= NETDEV_UNREGISTER
)
99 tr
= trelay_find(dev
);
103 trelay_do_remove(tr
);
109 static ssize_t
trelay_remove_write(struct file
*file
, const char __user
*ubuf
,
110 size_t count
, loff_t
*ppos
)
112 struct trelay
*tr
= file
->private_data
;
118 static int trelay_remove_release(struct inode
*inode
, struct file
*file
)
120 struct trelay
*tr
, *tmp
;
122 /* This is the only file op that is called outside debugfs_use_file_*()
123 * context which means that: (1) this file can be removed and
124 * (2) file->private_data may no longer be valid */
126 list_for_each_entry_safe(tr
, tmp
, &trelay_devs
, list
)
128 trelay_do_remove(tr
);
134 static const struct file_operations fops_remove
= {
135 .owner
= THIS_MODULE
,
137 .write
= trelay_remove_write
,
138 .llseek
= default_llseek
,
139 .release
= trelay_remove_release
,
143 static int trelay_do_add(char *name
, char *devn1
, char *devn2
)
145 struct net_device
*dev1
, *dev2
;
146 struct trelay
*tr
, *tr1
;
149 tr
= kzalloc(sizeof(*tr
) + strlen(name
) + 1, GFP_KERNEL
);
157 list_for_each_entry(tr1
, &trelay_devs
, list
) {
158 if (!strcmp(tr1
->name
, name
))
163 dev1
= dev_get_by_name_rcu(&init_net
, devn1
);
164 dev2
= dev_get_by_name_rcu(&init_net
, devn2
);
168 ret
= netdev_rx_handler_register(dev1
, trelay_handle_frame
, dev2
);
172 ret
= netdev_rx_handler_register(dev2
, trelay_handle_frame
, dev1
);
174 netdev_rx_handler_unregister(dev1
);
181 strcpy(tr
->name
, name
);
184 list_add_tail(&tr
->list
, &trelay_devs
);
186 tr
->debugfs
= debugfs_create_dir(name
, debugfs_dir
);
187 debugfs_create_file("remove", S_IWUSR
, tr
->debugfs
, tr
, &fops_remove
);
199 static ssize_t
trelay_add_write(struct file
*file
, const char __user
*ubuf
,
200 size_t count
, loff_t
*ppos
)
203 char *dev1
, *dev2
, *tmp
;
206 len
= min(count
, sizeof(buf
) - 1);
207 if (copy_from_user(buf
, ubuf
, len
))
212 if ((tmp
= strchr(buf
, '\n')))
215 dev1
= strchr(buf
, ',');
221 dev2
= strchr(dev1
, ',');
226 if (strchr(dev2
, ','))
229 if (!strlen(buf
) || !strlen(dev1
) || !strlen(dev2
))
232 ret
= trelay_do_add(buf
, dev1
, dev2
);
239 static const struct file_operations fops_add
= {
240 .owner
= THIS_MODULE
,
241 .write
= trelay_add_write
,
242 .llseek
= default_llseek
,
245 static struct notifier_block tr_dev_notifier
= {
246 .notifier_call
= tr_device_event
249 static int __init
trelay_init(void)
253 debugfs_dir
= debugfs_create_dir("trelay", NULL
);
257 debugfs_create_file("add", S_IWUSR
, debugfs_dir
, NULL
, &fops_add
);
259 ret
= register_netdevice_notifier(&tr_dev_notifier
);
266 debugfs_remove_recursive(debugfs_dir
);
270 static void __exit
trelay_exit(void)
272 struct trelay
*tr
, *tmp
;
274 unregister_netdevice_notifier(&tr_dev_notifier
);
277 list_for_each_entry_safe(tr
, tmp
, &trelay_devs
, list
)
278 trelay_do_remove(tr
);
281 debugfs_remove_recursive(debugfs_dir
);
284 module_init(trelay_init
);
285 module_exit(trelay_exit
);
286 MODULE_LICENSE("GPL");