1*4882a593Smuzhiyun /* Copyright 2011, Siemens AG
2*4882a593Smuzhiyun * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
3*4882a593Smuzhiyun */
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun /* Based on patches from Jon Smirl <jonsmirl@gmail.com>
6*4882a593Smuzhiyun * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
9*4882a593Smuzhiyun * it under the terms of the GNU General Public License version 2
10*4882a593Smuzhiyun * as published by the Free Software Foundation.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful,
13*4882a593Smuzhiyun * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*4882a593Smuzhiyun * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15*4882a593Smuzhiyun * GNU General Public License for more details.
16*4882a593Smuzhiyun */
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun /* Jon's code is based on 6lowpan implementation for Contiki which is:
19*4882a593Smuzhiyun * Copyright (c) 2008, Swedish Institute of Computer Science.
20*4882a593Smuzhiyun * All rights reserved.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * Redistribution and use in source and binary forms, with or without
23*4882a593Smuzhiyun * modification, are permitted provided that the following conditions
24*4882a593Smuzhiyun * are met:
25*4882a593Smuzhiyun * 1. Redistributions of source code must retain the above copyright
26*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer.
27*4882a593Smuzhiyun * 2. Redistributions in binary form must reproduce the above copyright
28*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer in the
29*4882a593Smuzhiyun * documentation and/or other materials provided with the distribution.
30*4882a593Smuzhiyun * 3. Neither the name of the Institute nor the names of its contributors
31*4882a593Smuzhiyun * may be used to endorse or promote products derived from this software
32*4882a593Smuzhiyun * without specific prior written permission.
33*4882a593Smuzhiyun *
34*4882a593Smuzhiyun * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
35*4882a593Smuzhiyun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36*4882a593Smuzhiyun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37*4882a593Smuzhiyun * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
38*4882a593Smuzhiyun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39*4882a593Smuzhiyun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40*4882a593Smuzhiyun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41*4882a593Smuzhiyun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42*4882a593Smuzhiyun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43*4882a593Smuzhiyun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44*4882a593Smuzhiyun * SUCH DAMAGE.
45*4882a593Smuzhiyun */
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #include <linux/module.h>
48*4882a593Smuzhiyun #include <linux/netdevice.h>
49*4882a593Smuzhiyun #include <linux/ieee802154.h>
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun #include <net/ipv6.h>
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun #include "6lowpan_i.h"
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun static int open_count;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static const struct header_ops lowpan_header_ops = {
58*4882a593Smuzhiyun .create = lowpan_header_create,
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun
lowpan_dev_init(struct net_device * ldev)61*4882a593Smuzhiyun static int lowpan_dev_init(struct net_device *ldev)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun netdev_lockdep_set_classes(ldev);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun return 0;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
lowpan_open(struct net_device * dev)68*4882a593Smuzhiyun static int lowpan_open(struct net_device *dev)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun if (!open_count)
71*4882a593Smuzhiyun lowpan_rx_init();
72*4882a593Smuzhiyun open_count++;
73*4882a593Smuzhiyun return 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
lowpan_stop(struct net_device * dev)76*4882a593Smuzhiyun static int lowpan_stop(struct net_device *dev)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun open_count--;
79*4882a593Smuzhiyun if (!open_count)
80*4882a593Smuzhiyun lowpan_rx_exit();
81*4882a593Smuzhiyun return 0;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
lowpan_neigh_construct(struct net_device * dev,struct neighbour * n)84*4882a593Smuzhiyun static int lowpan_neigh_construct(struct net_device *dev, struct neighbour *n)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /* default no short_addr is available for a neighbour */
89*4882a593Smuzhiyun neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
90*4882a593Smuzhiyun return 0;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
lowpan_get_iflink(const struct net_device * dev)93*4882a593Smuzhiyun static int lowpan_get_iflink(const struct net_device *dev)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun return lowpan_802154_dev(dev)->wdev->ifindex;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static const struct net_device_ops lowpan_netdev_ops = {
99*4882a593Smuzhiyun .ndo_init = lowpan_dev_init,
100*4882a593Smuzhiyun .ndo_start_xmit = lowpan_xmit,
101*4882a593Smuzhiyun .ndo_open = lowpan_open,
102*4882a593Smuzhiyun .ndo_stop = lowpan_stop,
103*4882a593Smuzhiyun .ndo_neigh_construct = lowpan_neigh_construct,
104*4882a593Smuzhiyun .ndo_get_iflink = lowpan_get_iflink,
105*4882a593Smuzhiyun };
106*4882a593Smuzhiyun
lowpan_setup(struct net_device * ldev)107*4882a593Smuzhiyun static void lowpan_setup(struct net_device *ldev)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
110*4882a593Smuzhiyun /* We need an ipv6hdr as minimum len when calling xmit */
111*4882a593Smuzhiyun ldev->hard_header_len = sizeof(struct ipv6hdr);
112*4882a593Smuzhiyun ldev->flags = IFF_BROADCAST | IFF_MULTICAST;
113*4882a593Smuzhiyun ldev->priv_flags |= IFF_NO_QUEUE;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun ldev->netdev_ops = &lowpan_netdev_ops;
116*4882a593Smuzhiyun ldev->header_ops = &lowpan_header_ops;
117*4882a593Smuzhiyun ldev->needs_free_netdev = true;
118*4882a593Smuzhiyun ldev->features |= NETIF_F_NETNS_LOCAL;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
lowpan_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)121*4882a593Smuzhiyun static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[],
122*4882a593Smuzhiyun struct netlink_ext_ack *extack)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun if (tb[IFLA_ADDRESS]) {
125*4882a593Smuzhiyun if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
126*4882a593Smuzhiyun return -EINVAL;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun return 0;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
lowpan_newlink(struct net * src_net,struct net_device * ldev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)131*4882a593Smuzhiyun static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
132*4882a593Smuzhiyun struct nlattr *tb[], struct nlattr *data[],
133*4882a593Smuzhiyun struct netlink_ext_ack *extack)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct net_device *wdev;
136*4882a593Smuzhiyun int ret;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun ASSERT_RTNL();
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun pr_debug("adding new link\n");
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (!tb[IFLA_LINK])
143*4882a593Smuzhiyun return -EINVAL;
144*4882a593Smuzhiyun /* find and hold wpan device */
145*4882a593Smuzhiyun wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK]));
146*4882a593Smuzhiyun if (!wdev)
147*4882a593Smuzhiyun return -ENODEV;
148*4882a593Smuzhiyun if (wdev->type != ARPHRD_IEEE802154) {
149*4882a593Smuzhiyun dev_put(wdev);
150*4882a593Smuzhiyun return -EINVAL;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (wdev->ieee802154_ptr->lowpan_dev) {
154*4882a593Smuzhiyun dev_put(wdev);
155*4882a593Smuzhiyun return -EBUSY;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun lowpan_802154_dev(ldev)->wdev = wdev;
159*4882a593Smuzhiyun /* Set the lowpan hardware address to the wpan hardware address. */
160*4882a593Smuzhiyun memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
161*4882a593Smuzhiyun /* We need headroom for possible wpan_dev_hard_header call and tailroom
162*4882a593Smuzhiyun * for encryption/fcs handling. The lowpan interface will replace
163*4882a593Smuzhiyun * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN
164*4882a593Smuzhiyun * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6
165*4882a593Smuzhiyun * header.
166*4882a593Smuzhiyun */
167*4882a593Smuzhiyun ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN +
168*4882a593Smuzhiyun wdev->needed_headroom;
169*4882a593Smuzhiyun ldev->needed_tailroom = wdev->needed_tailroom;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun ldev->neigh_priv_len = sizeof(struct lowpan_802154_neigh);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
174*4882a593Smuzhiyun if (ret < 0) {
175*4882a593Smuzhiyun dev_put(wdev);
176*4882a593Smuzhiyun return ret;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun wdev->ieee802154_ptr->lowpan_dev = ldev;
180*4882a593Smuzhiyun return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
lowpan_dellink(struct net_device * ldev,struct list_head * head)183*4882a593Smuzhiyun static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun struct net_device *wdev = lowpan_802154_dev(ldev)->wdev;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun ASSERT_RTNL();
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun wdev->ieee802154_ptr->lowpan_dev = NULL;
190*4882a593Smuzhiyun lowpan_unregister_netdevice(ldev);
191*4882a593Smuzhiyun dev_put(wdev);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
195*4882a593Smuzhiyun .kind = "lowpan",
196*4882a593Smuzhiyun .priv_size = LOWPAN_PRIV_SIZE(sizeof(struct lowpan_802154_dev)),
197*4882a593Smuzhiyun .setup = lowpan_setup,
198*4882a593Smuzhiyun .newlink = lowpan_newlink,
199*4882a593Smuzhiyun .dellink = lowpan_dellink,
200*4882a593Smuzhiyun .validate = lowpan_validate,
201*4882a593Smuzhiyun };
202*4882a593Smuzhiyun
lowpan_netlink_init(void)203*4882a593Smuzhiyun static inline int __init lowpan_netlink_init(void)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun return rtnl_link_register(&lowpan_link_ops);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
lowpan_netlink_fini(void)208*4882a593Smuzhiyun static inline void lowpan_netlink_fini(void)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun rtnl_link_unregister(&lowpan_link_ops);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
lowpan_device_event(struct notifier_block * unused,unsigned long event,void * ptr)213*4882a593Smuzhiyun static int lowpan_device_event(struct notifier_block *unused,
214*4882a593Smuzhiyun unsigned long event, void *ptr)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
217*4882a593Smuzhiyun struct wpan_dev *wpan_dev;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (ndev->type != ARPHRD_IEEE802154)
220*4882a593Smuzhiyun return NOTIFY_DONE;
221*4882a593Smuzhiyun wpan_dev = ndev->ieee802154_ptr;
222*4882a593Smuzhiyun if (!wpan_dev)
223*4882a593Smuzhiyun return NOTIFY_DONE;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun switch (event) {
226*4882a593Smuzhiyun case NETDEV_UNREGISTER:
227*4882a593Smuzhiyun /* Check if wpan interface is unregistered that we
228*4882a593Smuzhiyun * also delete possible lowpan interfaces which belongs
229*4882a593Smuzhiyun * to the wpan interface.
230*4882a593Smuzhiyun */
231*4882a593Smuzhiyun if (wpan_dev->lowpan_dev)
232*4882a593Smuzhiyun lowpan_dellink(wpan_dev->lowpan_dev, NULL);
233*4882a593Smuzhiyun break;
234*4882a593Smuzhiyun default:
235*4882a593Smuzhiyun return NOTIFY_DONE;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun return NOTIFY_OK;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun static struct notifier_block lowpan_dev_notifier = {
242*4882a593Smuzhiyun .notifier_call = lowpan_device_event,
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun
lowpan_init_module(void)245*4882a593Smuzhiyun static int __init lowpan_init_module(void)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun int err = 0;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun err = lowpan_net_frag_init();
250*4882a593Smuzhiyun if (err < 0)
251*4882a593Smuzhiyun goto out;
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun err = lowpan_netlink_init();
254*4882a593Smuzhiyun if (err < 0)
255*4882a593Smuzhiyun goto out_frag;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun err = register_netdevice_notifier(&lowpan_dev_notifier);
258*4882a593Smuzhiyun if (err < 0)
259*4882a593Smuzhiyun goto out_pack;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun return 0;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun out_pack:
264*4882a593Smuzhiyun lowpan_netlink_fini();
265*4882a593Smuzhiyun out_frag:
266*4882a593Smuzhiyun lowpan_net_frag_exit();
267*4882a593Smuzhiyun out:
268*4882a593Smuzhiyun return err;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun
lowpan_cleanup_module(void)271*4882a593Smuzhiyun static void __exit lowpan_cleanup_module(void)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun lowpan_netlink_fini();
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun lowpan_net_frag_exit();
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun unregister_netdevice_notifier(&lowpan_dev_notifier);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun module_init(lowpan_init_module);
281*4882a593Smuzhiyun module_exit(lowpan_cleanup_module);
282*4882a593Smuzhiyun MODULE_LICENSE("GPL");
283*4882a593Smuzhiyun MODULE_ALIAS_RTNL_LINK("lowpan");
284