Added RCU sync on unloading
[ana-net.git] / src / fb_eth.c
blob018fc8805f6a9e0af216c8d3f72052a7dd9f6396
1 /*
2 * Lightweight Autonomic Network Architecture
4 * Eth/PHY layer. Redirects all traffic into the LANA stack.
5 * Singleton object.
7 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
8 * Swiss federal institute of technology (ETH Zurich)
9 * Subject to the GPL.
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/notifier.h>
16 #include <linux/if_ether.h>
17 #include <linux/if_arp.h>
18 #include <linux/if.h>
19 #include <linux/etherdevice.h>
20 #include <linux/rtnetlink.h>
21 #include <linux/seqlock.h>
23 #include "xt_idp.h"
24 #include "xt_engine.h"
25 #include "xt_skb.h"
26 #include "xt_fblock.h"
27 #include "xt_builder.h"
28 #include "xt_vlink.h"
30 #define IFF_VLINK_MAS 0x20000
31 #define IFF_VLINK_DEV 0x40000
32 #define IFF_IS_BRIDGED 0x60000
34 struct fb_eth_priv {
35 idp_t port[2];
36 seqlock_t lock;
37 struct net_device *dev;
40 static LIST_HEAD(fb_eth_devs);
41 static DEFINE_SPINLOCK(fb_eth_devs_lock);
43 struct fb_eth_dev_node {
44 struct list_head list;
45 struct fblock *fb;
46 struct net_device *dev;
49 static inline int fb_eth_dev_is_bridged(struct net_device *dev)
51 return (dev->priv_flags & IFF_IS_BRIDGED) == IFF_IS_BRIDGED;
54 static inline int fb_ethvlink_real_dev_is_hooked(struct net_device *dev)
56 return (dev->priv_flags & IFF_VLINK_MAS) == IFF_VLINK_MAS;
59 static inline void fb_eth_make_dev_bridged(struct net_device *dev)
61 dev->priv_flags |= IFF_IS_BRIDGED;
64 static inline void fb_eth_make_dev_unbridged(struct net_device *dev)
66 dev->priv_flags &= ~IFF_IS_BRIDGED;
69 static rx_handler_result_t fb_eth_handle_frame(struct sk_buff **pskb)
71 unsigned int seq;
72 struct sk_buff *skb = *pskb;
73 struct fb_eth_dev_node *node;
74 struct fblock *fb = NULL;
75 struct fb_eth_priv __percpu *fb_priv_cpu;
77 if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
78 return RX_HANDLER_PASS;
79 if (unlikely(!is_valid_ether_addr(eth_hdr(skb)->h_source)))
80 goto drop;
81 skb = skb_share_check(skb, GFP_ATOMIC);
82 if (unlikely(!skb))
83 return RX_HANDLER_CONSUMED;
85 list_for_each_entry_rcu(node, &fb_eth_devs, list)
86 if (skb->dev == node->dev)
87 fb = node->fb;
88 if (!fb)
89 goto drop;
91 skb_orphan(skb);
93 fb_priv_cpu = this_cpu_ptr(rcu_dereference(fb->private_data));
94 if (fb_priv_cpu->port[TYPE_INGRESS] == IDP_UNKNOWN)
95 goto drop;
97 do {
98 seq = read_seqbegin(&fb_priv_cpu->lock);
99 write_next_idp_to_skb(skb, fb->idp,
100 fb_priv_cpu->port[TYPE_INGRESS]);
101 } while (read_seqretry(&fb_priv_cpu->lock, seq));
103 process_packet(skb, TYPE_INGRESS);
105 return RX_HANDLER_CONSUMED;
106 drop:
107 kfree_skb(skb);
108 return RX_HANDLER_CONSUMED;
111 static int fb_eth_netrx(const struct fblock * const fb,
112 struct sk_buff * const skb,
113 enum path_type * const dir)
115 struct fb_eth_priv __percpu *fb_priv_cpu;
116 fb_priv_cpu = this_cpu_ptr(rcu_dereference(fb->private_data));
117 write_next_idp_to_skb(skb, fb->idp, IDP_UNKNOWN);
118 skb->dev = fb_priv_cpu->dev;
119 dev_queue_xmit(skb);
120 return PPE_DROPPED;
123 static int fb_eth_event(struct notifier_block *self, unsigned long cmd,
124 void *args)
126 int ret = NOTIFY_OK;
127 unsigned int cpu;
128 struct fblock *fb;
129 struct fb_eth_priv __percpu *fb_priv;
131 rcu_read_lock();
132 fb = rcu_dereference_raw(container_of(self, struct fblock_notifier, nb)->self);
133 fb_priv = (struct fb_eth_priv __percpu *) rcu_dereference_raw(fb->private_data);
134 rcu_read_unlock();
136 switch (cmd) {
137 case FBLOCK_BIND_IDP: {
138 int bound = 0;
139 struct fblock_bind_msg *msg = args;
140 get_online_cpus();
141 for_each_online_cpu(cpu) {
142 struct fb_eth_priv *fb_priv_cpu;
143 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
144 if (fb_priv_cpu->port[msg->dir] == IDP_UNKNOWN) {
145 write_seqlock(&fb_priv_cpu->lock);
146 fb_priv_cpu->port[msg->dir] = msg->idp;
147 write_sequnlock(&fb_priv_cpu->lock);
148 bound = 1;
149 } else {
150 ret = NOTIFY_BAD;
151 break;
154 put_online_cpus();
155 if (bound)
156 printk(KERN_INFO "[%s::vlink] port %s bound to IDP%u\n",
157 fb->name, path_names[msg->dir], msg->idp);
158 } break;
159 case FBLOCK_UNBIND_IDP: {
160 int unbound = 0;
161 struct fblock_bind_msg *msg = args;
162 get_online_cpus();
163 for_each_online_cpu(cpu) {
164 struct fb_eth_priv *fb_priv_cpu;
165 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
166 if (fb_priv_cpu->port[msg->dir] == msg->idp) {
167 write_seqlock(&fb_priv_cpu->lock);
168 fb_priv_cpu->port[msg->dir] = IDP_UNKNOWN;
169 write_sequnlock(&fb_priv_cpu->lock);
170 unbound = 1;
171 } else {
172 ret = NOTIFY_BAD;
173 break;
176 put_online_cpus();
177 if (unbound)
178 printk(KERN_INFO "[%s::vlink] port %s unbound\n",
179 fb->name, path_names[msg->dir]);
180 } break;
181 default:
182 break;
185 return ret;
188 static void cleanup_fb_eth(struct net_device *dev)
190 rtnl_lock();
191 if (fb_eth_dev_is_bridged(dev)) {
192 netdev_rx_handler_unregister(dev);
193 fb_eth_make_dev_unbridged(dev);
195 rtnl_unlock();
198 static int init_fb_eth(struct net_device *dev)
200 int ret = 0;
201 rtnl_lock();
202 ret = netdev_rx_handler_register(dev, fb_eth_handle_frame, NULL);
203 if (ret)
204 ret = -EIO;
205 else
206 fb_eth_make_dev_bridged(dev);
207 rtnl_unlock();
208 return ret;
211 static struct fblock *fb_eth_build_fblock(struct net_device *dev)
213 int ret = 0;
214 unsigned int cpu;
215 struct fblock *fb;
216 struct fb_eth_priv __percpu *fb_priv;
218 fb = alloc_fblock(GFP_ATOMIC);
219 if (!fb)
220 return NULL;
222 fb_priv = alloc_percpu(struct fb_eth_priv);
223 if (!fb_priv)
224 goto err;
226 get_online_cpus();
227 for_each_online_cpu(cpu) {
228 struct fb_eth_priv *fb_priv_cpu;
229 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
230 seqlock_init(&fb_priv_cpu->lock);
231 fb_priv_cpu->port[0] = IDP_UNKNOWN;
232 fb_priv_cpu->port[1] = IDP_UNKNOWN;
233 fb_priv_cpu->dev = dev;
235 put_online_cpus();
237 ret = init_fblock(fb, dev->name, fb_priv);
238 if (ret)
239 goto err2;
241 fb->netfb_rx = fb_eth_netrx;
242 fb->event_rx = fb_eth_event;
243 fb->factory = NULL;
245 ret = register_fblock_namespace(fb);
246 if (ret)
247 goto err3;
248 ret = init_fb_eth(dev);
249 if (ret)
250 goto err4;
251 __module_get(THIS_MODULE);
252 smp_wmb();
253 return fb;
254 err4:
255 unregister_fblock_namespace_no_rcu(fb);
256 err3:
257 cleanup_fblock_ctor(fb);
258 err2:
259 free_percpu(fb_priv);
260 err:
261 kfree_fblock(fb);
262 fb = NULL;
263 return NULL;
266 static void fb_eth_destroy_fblock(struct fblock *fb)
268 struct fb_eth_priv __percpu *fb_priv_cpu;
270 rcu_read_lock();
271 fb_priv_cpu = this_cpu_ptr(rcu_dereference(fb->private_data));
272 cleanup_fb_eth(fb_priv_cpu->dev);
273 rcu_read_unlock();
275 unregister_fblock_namespace_no_rcu(fb);
276 cleanup_fblock(fb);
277 free_percpu(rcu_dereference_raw(fb->private_data));
278 kfree_fblock(fb);
279 module_put(THIS_MODULE);
282 static struct vlink_subsys fb_eth_sys __read_mostly = {
283 .name = "eth",
284 .owner = THIS_MODULE,
285 .type = VLINKNLGRP_ETHERNET,
286 .rwsem = __RWSEM_INITIALIZER(fb_eth_sys.rwsem),
289 static int fb_eth_start_hook_dev(struct vlinknlmsg *vhdr, struct nlmsghdr *nlh)
291 unsigned long flags;
292 struct net_device *dev;
293 struct fb_eth_dev_node *node;
295 if (vhdr->cmd != VLINKNLCMD_START_HOOK_DEVICE)
296 return NETLINK_VLINK_RX_NXT;
298 dev = dev_get_by_name(&init_net, vhdr->real_name);
299 if (dev && (dev->priv_flags & IFF_VLINK_DEV) == IFF_VLINK_DEV)
300 goto err;
301 else if (!dev)
302 return NETLINK_VLINK_RX_EMERG;
304 if (fb_eth_dev_is_bridged(dev))
305 goto out;
306 if (fb_ethvlink_real_dev_is_hooked(dev))
307 goto out;
309 node = kmalloc(sizeof(*node), GFP_KERNEL);
310 if (!node)
311 goto out;
312 node->dev = dev;
313 node->fb = fb_eth_build_fblock(dev);
314 if (!node->fb) {
315 kfree(node);
316 goto out;
319 spin_lock_irqsave(&fb_eth_devs_lock, flags);
320 list_add_rcu(&node->list, &fb_eth_devs);
321 spin_unlock_irqrestore(&fb_eth_devs_lock, flags);
323 printk(KERN_INFO "[lana] hook attached to carrier %s\n",
324 vhdr->real_name);
325 out:
326 dev_put(dev);
327 return NETLINK_VLINK_RX_STOP;
328 err:
329 dev_put(dev);
330 return NETLINK_VLINK_RX_EMERG;
333 static int fb_eth_stop_hook_dev(struct vlinknlmsg *vhdr, struct nlmsghdr *nlh)
335 unsigned long flags;
336 struct fblock *fb = NULL;
337 struct net_device *dev;
338 struct fb_eth_dev_node *node;
340 if (vhdr->cmd != VLINKNLCMD_STOP_HOOK_DEVICE)
341 return NETLINK_VLINK_RX_NXT;
343 dev = dev_get_by_name(&init_net, vhdr->real_name);
344 if (!dev)
345 return NETLINK_VLINK_RX_EMERG;
346 if (!fb_eth_dev_is_bridged(dev))
347 goto err_put;
348 if ((dev->flags & IFF_RUNNING) == IFF_RUNNING)
349 goto err_put;
351 spin_lock_irqsave(&fb_eth_devs_lock, flags);
352 list_for_each_entry_rcu(node, &fb_eth_devs, list) {
353 if (dev == node->dev) {
354 fb = node->fb;
355 break;
358 spin_unlock_irqrestore(&fb_eth_devs_lock, flags);
360 if (!fb)
361 goto err_put;
362 if (atomic_read(&fb->refcnt) > 2) {
363 printk(KERN_INFO "Cannot remove vlink dev! Still in use by "
364 "others!\n");
365 goto err_put;
368 spin_lock_irqsave(&fb_eth_devs_lock, flags);
369 list_del_rcu(&node->list);
370 spin_unlock_irqrestore(&fb_eth_devs_lock, flags);
372 fb_eth_destroy_fblock(fb);
373 synchronize_rcu();
374 kfree(node);
376 printk(KERN_INFO "[lana] hook detached from carrier %s\n",
377 vhdr->real_name);
379 dev_put(dev);
380 return NETLINK_VLINK_RX_STOP;
381 err_put:
382 dev_put(dev);
383 return NETLINK_VLINK_RX_EMERG;
386 static struct vlink_callback fb_eth_start_hook_dev_cb =
387 VLINK_CALLBACK_INIT(fb_eth_start_hook_dev, NETLINK_VLINK_PRIO_HIGH);
388 static struct vlink_callback fb_eth_stop_hook_dev_cb =
389 VLINK_CALLBACK_INIT(fb_eth_stop_hook_dev, NETLINK_VLINK_PRIO_HIGH);
391 static int __init init_fb_eth_module(void)
393 int ret = 0;
394 ret = vlink_subsys_register(&fb_eth_sys);
395 if (ret)
396 return ret;
398 vlink_add_callback(&fb_eth_sys, &fb_eth_start_hook_dev_cb);
399 vlink_add_callback(&fb_eth_sys, &fb_eth_stop_hook_dev_cb);
401 printk(KERN_INFO "[lana] Ethernet vlink layer loaded!\n");
402 return ret;
405 static void __exit cleanup_fb_eth_module(void)
407 synchronize_rcu();
408 vlink_subsys_unregister_batch(&fb_eth_sys);
409 printk(KERN_INFO "[lana] Ethernet vlink layer removed!\n");
412 module_init(init_fb_eth_module);
413 module_exit(cleanup_fb_eth_module);
415 MODULE_LICENSE("GPL");
416 MODULE_AUTHOR("Daniel Borkmann <dborkma@tik.ee.ethz.ch>");
417 MODULE_DESCRIPTION("Ethernet virtual link layer driver");