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