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 #define trelay_log(loglevel, tr, fmt, ...) \
24 printk(loglevel "trelay: %s <-> %s: " fmt "\n", \
25 tr->dev1->name, tr->dev2->name, ##__VA_ARGS__);
27 static LIST_HEAD(trelay_devs
);
28 static struct dentry
*debugfs_dir
;
31 struct list_head list
;
32 struct net_device
*dev1
, *dev2
;
33 struct dentry
*debugfs
;
38 rx_handler_result_t
trelay_handle_frame(struct sk_buff
**pskb
)
40 struct net_device
*dev
;
41 struct sk_buff
*skb
= *pskb
;
43 dev
= rcu_dereference(skb
->dev
->rx_handler_data
);
45 return RX_HANDLER_PASS
;
47 if (skb
->protocol
== htons(ETH_P_PAE
))
48 return RX_HANDLER_PASS
;
50 skb_push(skb
, ETH_HLEN
);
52 skb_forward_csum(skb
);
55 return RX_HANDLER_CONSUMED
;
58 static int trelay_open(struct inode
*inode
, struct file
*file
)
60 file
->private_data
= inode
->i_private
;
64 static int trelay_do_remove(struct trelay
*tr
)
68 /* First and before all, ensure that the debugfs file is removed
69 * to prevent dangling pointer in file->private_data */
70 debugfs_remove_recursive(tr
->debugfs
);
75 netdev_rx_handler_unregister(tr
->dev1
);
76 netdev_rx_handler_unregister(tr
->dev2
);
78 trelay_log(KERN_INFO
, tr
, "stopped");
85 static struct trelay
*trelay_find(struct net_device
*dev
)
89 list_for_each_entry(tr
, &trelay_devs
, list
) {
90 if (tr
->dev1
== dev
|| tr
->dev2
== dev
)
96 static int tr_device_event(struct notifier_block
*unused
, unsigned long event
,
99 struct net_device
*dev
= netdev_notifier_info_to_dev(ptr
);
102 if (event
!= NETDEV_UNREGISTER
)
105 tr
= trelay_find(dev
);
109 trelay_do_remove(tr
);
115 static ssize_t
trelay_remove_write(struct file
*file
, const char __user
*ubuf
,
116 size_t count
, loff_t
*ppos
)
118 struct trelay
*tr
= file
->private_data
;
124 static int trelay_remove_release(struct inode
*inode
, struct file
*file
)
126 struct trelay
*tr
, *tmp
;
128 /* This is the only file op that is called outside debugfs_use_file_*()
129 * context which means that: (1) this file can be removed and
130 * (2) file->private_data may no longer be valid */
132 list_for_each_entry_safe(tr
, tmp
, &trelay_devs
, list
)
134 trelay_do_remove(tr
);
140 static const struct file_operations fops_remove
= {
141 .owner
= THIS_MODULE
,
143 .write
= trelay_remove_write
,
144 .llseek
= default_llseek
,
145 .release
= trelay_remove_release
,
149 static int trelay_do_add(char *name
, char *devn1
, char *devn2
)
151 struct net_device
*dev1
, *dev2
;
152 struct trelay
*tr
, *tr1
;
155 tr
= kzalloc(sizeof(*tr
) + strlen(name
) + 1, GFP_KERNEL
);
163 list_for_each_entry(tr1
, &trelay_devs
, list
) {
164 if (!strcmp(tr1
->name
, name
))
169 dev1
= dev_get_by_name_rcu(&init_net
, devn1
);
170 dev2
= dev_get_by_name_rcu(&init_net
, devn2
);
174 ret
= netdev_rx_handler_register(dev1
, trelay_handle_frame
, dev2
);
178 ret
= netdev_rx_handler_register(dev2
, trelay_handle_frame
, dev1
);
180 netdev_rx_handler_unregister(dev1
);
187 strcpy(tr
->name
, name
);
190 list_add_tail(&tr
->list
, &trelay_devs
);
192 trelay_log(KERN_INFO
, tr
, "started");
194 tr
->debugfs
= debugfs_create_dir(name
, debugfs_dir
);
195 debugfs_create_file("remove", S_IWUSR
, tr
->debugfs
, tr
, &fops_remove
);
207 static ssize_t
trelay_add_write(struct file
*file
, const char __user
*ubuf
,
208 size_t count
, loff_t
*ppos
)
211 char *dev1
, *dev2
, *tmp
;
214 len
= min(count
, sizeof(buf
) - 1);
215 if (copy_from_user(buf
, ubuf
, len
))
220 if ((tmp
= strchr(buf
, '\n')))
223 dev1
= strchr(buf
, ',');
229 dev2
= strchr(dev1
, ',');
234 if (strchr(dev2
, ','))
237 if (!strlen(buf
) || !strlen(dev1
) || !strlen(dev2
))
240 ret
= trelay_do_add(buf
, dev1
, dev2
);
247 static const struct file_operations fops_add
= {
248 .owner
= THIS_MODULE
,
249 .write
= trelay_add_write
,
250 .llseek
= default_llseek
,
253 static struct notifier_block tr_dev_notifier
= {
254 .notifier_call
= tr_device_event
257 static int __init
trelay_init(void)
261 debugfs_dir
= debugfs_create_dir("trelay", NULL
);
265 debugfs_create_file("add", S_IWUSR
, debugfs_dir
, NULL
, &fops_add
);
267 ret
= register_netdevice_notifier(&tr_dev_notifier
);
274 debugfs_remove_recursive(debugfs_dir
);
278 static void __exit
trelay_exit(void)
280 struct trelay
*tr
, *tmp
;
282 unregister_netdevice_notifier(&tr_dev_notifier
);
285 list_for_each_entry_safe(tr
, tmp
, &trelay_devs
, list
)
286 trelay_do_remove(tr
);
289 debugfs_remove_recursive(debugfs_dir
);
292 module_init(trelay_init
);
293 module_exit(trelay_exit
);
294 MODULE_LICENSE("GPL");