added comment
[ana-net.git] / src / fb_eth.c
blob96ce7b226f443488f84147e0ed4dac5003732312
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_IS_BRIDGED 0x60000
32 struct fb_eth_priv {
33 idp_t port[2];
34 seqlock_t lock;
37 static int instantiated = 0;
39 static struct fblock *fb;
41 static inline int fb_eth_dev_is_bridged(struct net_device *dev)
43 return (dev->priv_flags & IFF_IS_BRIDGED) == IFF_IS_BRIDGED;
46 static inline void fb_eth_make_dev_bridged(struct net_device *dev)
48 dev->priv_flags |= IFF_IS_BRIDGED;
51 static inline void fb_eth_make_dev_unbridged(struct net_device *dev)
53 dev->priv_flags &= ~IFF_IS_BRIDGED;
56 static rx_handler_result_t fb_eth_handle_frame(struct sk_buff **pskb)
58 unsigned int seq;
59 struct sk_buff *skb = *pskb;
60 struct fb_eth_priv __percpu *fb_priv_cpu;
62 if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
63 return RX_HANDLER_PASS;
64 if (unlikely(!is_valid_ether_addr(eth_hdr(skb)->h_source)))
65 goto drop;
66 skb = skb_share_check(skb, GFP_ATOMIC);
67 if (unlikely(!skb))
68 return RX_HANDLER_CONSUMED;
70 fb_priv_cpu = this_cpu_ptr(rcu_dereference(fb->private_data));
71 do {
72 seq = read_seqbegin(&fb_priv_cpu->lock);
73 write_next_idp_to_skb(skb, fb->idp, 1
74 /*fb_priv_cpu->port[TYPE_INGRESS]*/);
75 } while (read_seqretry(&fb_priv_cpu->lock, seq));
77 process_packet(skb, TYPE_INGRESS);
79 return RX_HANDLER_CONSUMED;
80 drop:
81 kfree_skb(skb);
82 return RX_HANDLER_CONSUMED;
85 static int fb_eth_netrx(const struct fblock * const fb,
86 struct sk_buff * const skb,
87 enum path_type * const dir)
89 if (!skb->dev) {
90 kfree_skb(skb);
91 return PPE_DROPPED;
93 write_next_idp_to_skb(skb, fb->idp, IDP_UNKNOWN);
94 dev_queue_xmit(skb);
95 return PPE_DROPPED;
98 static int fb_eth_event(struct notifier_block *self, unsigned long cmd,
99 void *args)
101 int ret = NOTIFY_OK;
102 unsigned int cpu;
103 struct fb_eth_priv __percpu *fb_priv;
105 rcu_read_lock();
106 fb_priv = (struct fb_eth_priv __percpu *) rcu_dereference_raw(fb->private_data);
107 rcu_read_unlock();
109 switch (cmd) {
110 case FBLOCK_BIND_IDP: {
111 int bound = 0;
112 struct fblock_bind_msg *msg = args;
113 get_online_cpus();
114 for_each_online_cpu(cpu) {
115 struct fb_eth_priv *fb_priv_cpu;
116 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
117 if (fb_priv_cpu->port[msg->dir] == IDP_UNKNOWN) {
118 write_seqlock(&fb_priv_cpu->lock);
119 fb_priv_cpu->port[msg->dir] = msg->idp;
120 write_sequnlock(&fb_priv_cpu->lock);
121 bound = 1;
122 } else {
123 ret = NOTIFY_BAD;
124 break;
127 put_online_cpus();
128 if (bound)
129 printk(KERN_INFO "[%s::%s] port %s bound to IDP%u\n",
130 fb->name, fb->factory->type,
131 path_names[msg->dir], msg->idp);
132 } break;
133 case FBLOCK_UNBIND_IDP: {
134 int unbound = 0;
135 struct fblock_bind_msg *msg = args;
136 get_online_cpus();
137 for_each_online_cpu(cpu) {
138 struct fb_eth_priv *fb_priv_cpu;
139 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
140 if (fb_priv_cpu->port[msg->dir] == msg->idp) {
141 write_seqlock(&fb_priv_cpu->lock);
142 fb_priv_cpu->port[msg->dir] = IDP_UNKNOWN;
143 write_sequnlock(&fb_priv_cpu->lock);
144 unbound = 1;
145 } else {
146 ret = NOTIFY_BAD;
147 break;
150 put_online_cpus();
151 if (unbound)
152 printk(KERN_INFO "[%s::%s] port %s unbound\n",
153 fb->name, fb->factory->type,
154 path_names[msg->dir]);
155 } break;
156 default:
157 break;
160 return ret;
163 static void cleanup_fb_eth(void)
165 /* XXX: sometimes BUG, scheduling while atomic */
166 struct net_device *dev;
167 rtnl_lock();
168 for_each_netdev(&init_net, dev) {
169 if (fb_eth_dev_is_bridged(dev)) {
170 netdev_rx_handler_unregister(dev);
171 fb_eth_make_dev_unbridged(dev);
174 rtnl_unlock();
177 static int init_fb_eth(void)
179 int ret = 0, err = 0;
180 struct net_device *dev;
181 rtnl_lock();
182 for_each_netdev(&init_net, dev) {
183 ret = netdev_rx_handler_register(dev, fb_eth_handle_frame,
184 NULL);
185 if (ret) {
186 err = 1;
187 break;
189 fb_eth_make_dev_bridged(dev);
191 rtnl_unlock();
192 if (err) {
193 cleanup_fb_eth();
194 return ret;
196 return 0;
199 static struct fblock *fb_eth_ctor(char *name)
201 int ret = 0;
202 unsigned int cpu;
203 struct fb_eth_priv __percpu *fb_priv;
205 if (instantiated)
206 return NULL;
207 fb = alloc_fblock(GFP_ATOMIC);
208 if (!fb)
209 return NULL;
211 fb_priv = alloc_percpu(struct fb_eth_priv);
212 if (!fb_priv)
213 goto err;
215 get_online_cpus();
216 for_each_online_cpu(cpu) {
217 struct fb_eth_priv *fb_priv_cpu;
218 fb_priv_cpu = per_cpu_ptr(fb_priv, cpu);
219 seqlock_init(&fb_priv_cpu->lock);
220 fb_priv_cpu->port[0] = IDP_UNKNOWN;
221 fb_priv_cpu->port[1] = IDP_UNKNOWN;
223 put_online_cpus();
225 ret = init_fblock(fb, name, fb_priv);
226 if (ret)
227 goto err2;
228 fb->netfb_rx = fb_eth_netrx;
229 fb->event_rx = fb_eth_event;
230 ret = register_fblock_namespace(fb);
231 if (ret)
232 goto err3;
233 ret = init_fb_eth();
234 if (ret)
235 goto err4;
236 __module_get(THIS_MODULE);
237 instantiated = 1;
238 smp_wmb();
239 return fb;
240 err4:
241 unregister_fblock_namespace(fb);
242 return NULL;
243 err3:
244 cleanup_fblock_ctor(fb);
245 err2:
246 free_percpu(fb_priv);
247 err:
248 kfree_fblock(fb);
249 fb = NULL;
250 return NULL;
253 static void fb_eth_dtor(struct fblock *fb)
255 free_percpu(rcu_dereference_raw(fb->private_data));
256 module_put(THIS_MODULE);
257 instantiated = 0;
258 cleanup_fb_eth();
261 static struct fblock_factory fb_eth_factory = {
262 .type = "eth",
263 .mode = MODE_SOURCE,
264 .ctor = fb_eth_ctor,
265 .dtor = fb_eth_dtor,
266 .owner = THIS_MODULE,
269 static struct vlink_subsys fb_eth_sys __read_mostly = {
270 .name = "eth",
271 .type = VLINKNLGRP_ETHERNET,
272 .rwsem = __RWSEM_INITIALIZER(fb_eth_sys.rwsem),
275 static int fb_eth_start_hook_dev(struct vlinknlmsg *vhdr, struct nlmsghdr *nlh)
277 return NETLINK_VLINK_RX_NXT;
280 static int fb_eth_stop_hook_dev(struct vlinknlmsg *vhdr, struct nlmsghdr *nlh)
282 return NETLINK_VLINK_RX_NXT;
285 static struct vlink_callback fb_eth_start_hook_dev_cb =
286 VLINK_CALLBACK_INIT(fb_eth_start_hook_dev, NETLINK_VLINK_PRIO_HIGH);
287 static struct vlink_callback fb_eth_stop_hook_dev_cb =
288 VLINK_CALLBACK_INIT(fb_eth_stop_hook_dev, NETLINK_VLINK_PRIO_HIGH);
290 static int __init init_fb_eth_module(void)
292 int ret = 0;
293 ret = vlink_subsys_register(&fb_eth_sys);
294 if (ret)
295 return ret;
297 vlink_add_callback(&fb_eth_sys, &fb_eth_start_hook_dev_cb);
298 vlink_add_callback(&fb_eth_sys, &fb_eth_stop_hook_dev_cb);
300 ret = register_fblock_type(&fb_eth_factory);
301 if (ret)
302 vlink_subsys_unregister_batch(&fb_eth_sys);
303 return ret;
306 static void __exit cleanup_fb_eth_module(void)
308 unregister_fblock_type(&fb_eth_factory);
309 vlink_subsys_unregister_batch(&fb_eth_sys);
310 vlink_subsys_unregister_batch(&fb_eth_sys);
313 module_init(init_fb_eth_module);
314 module_exit(cleanup_fb_eth_module);
316 MODULE_LICENSE("GPL");
317 MODULE_AUTHOR("Daniel Borkmann <dborkma@tik.ee.ethz.ch>");
318 MODULE_DESCRIPTION("Ethernet/PHY link layer bridge driver");