]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/ieee802154/nl802154.c
5dec0bb5bb55510859239755f8ce5845d304e448
[karo-tx-linux.git] / net / ieee802154 / nl802154.c
1 /* This program is free software; you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License version 2
3  * as published by the Free Software Foundation.
4  *
5  * This program is distributed in the hope that it will be useful,
6  * but WITHOUT ANY WARRANTY; without even the implied warranty of
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8  * GNU General Public License for more details.
9  *
10  * Authors:
11  * Alexander Aring <aar@pengutronix.de>
12  *
13  * Based on: net/wireless/nl80211.c
14  */
15
16 #include <linux/rtnetlink.h>
17
18 #include <net/cfg802154.h>
19 #include <net/genetlink.h>
20 #include <net/mac802154.h>
21 #include <net/netlink.h>
22 #include <net/nl802154.h>
23 #include <net/sock.h>
24
25 #include "nl802154.h"
26 #include "core.h"
27
28 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
29                              struct genl_info *info);
30
31 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
32                                struct genl_info *info);
33
34 /* the netlink family */
35 static struct genl_family nl802154_fam = {
36         .id = GENL_ID_GENERATE,         /* don't bother with a hardcoded ID */
37         .name = NL802154_GENL_NAME,     /* have users key off the name instead */
38         .hdrsize = 0,                   /* no private header */
39         .version = 1,                   /* no particular meaning now */
40         .maxattr = NL802154_ATTR_MAX,
41         .netnsok = true,
42         .pre_doit = nl802154_pre_doit,
43         .post_doit = nl802154_post_doit,
44 };
45
46 /* multicast groups */
47 enum nl802154_multicast_groups {
48         NL802154_MCGRP_CONFIG,
49 };
50
51 static const struct genl_multicast_group nl802154_mcgrps[] = {
52         [NL802154_MCGRP_CONFIG] = { .name = "config", },
53 };
54
55 /* returns ERR_PTR values */
56 static struct wpan_dev *
57 __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
58 {
59         struct cfg802154_registered_device *rdev;
60         struct wpan_dev *result = NULL;
61         bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
62         bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
63         u64 wpan_dev_id;
64         int wpan_phy_idx = -1;
65         int ifidx = -1;
66
67         ASSERT_RTNL();
68
69         if (!have_ifidx && !have_wpan_dev_id)
70                 return ERR_PTR(-EINVAL);
71
72         if (have_ifidx)
73                 ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
74         if (have_wpan_dev_id) {
75                 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
76                 wpan_phy_idx = wpan_dev_id >> 32;
77         }
78
79         list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
80                 struct wpan_dev *wpan_dev;
81
82                 /* TODO netns compare */
83
84                 if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
85                         continue;
86
87                 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
88                         if (have_ifidx && wpan_dev->netdev &&
89                             wpan_dev->netdev->ifindex == ifidx) {
90                                 result = wpan_dev;
91                                 break;
92                         }
93                         if (have_wpan_dev_id &&
94                             wpan_dev->identifier == (u32)wpan_dev_id) {
95                                 result = wpan_dev;
96                                 break;
97                         }
98                 }
99
100                 if (result)
101                         break;
102         }
103
104         if (result)
105                 return result;
106
107         return ERR_PTR(-ENODEV);
108 }
109
110 static struct cfg802154_registered_device *
111 __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
112 {
113         struct cfg802154_registered_device *rdev = NULL, *tmp;
114         struct net_device *netdev;
115
116         ASSERT_RTNL();
117
118         if (!attrs[NL802154_ATTR_WPAN_PHY] &&
119             !attrs[NL802154_ATTR_IFINDEX] &&
120             !attrs[NL802154_ATTR_WPAN_DEV])
121                 return ERR_PTR(-EINVAL);
122
123         if (attrs[NL802154_ATTR_WPAN_PHY])
124                 rdev = cfg802154_rdev_by_wpan_phy_idx(
125                                 nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
126
127         if (attrs[NL802154_ATTR_WPAN_DEV]) {
128                 u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
129                 struct wpan_dev *wpan_dev;
130                 bool found = false;
131
132                 tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
133                 if (tmp) {
134                         /* make sure wpan_dev exists */
135                         list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
136                                 if (wpan_dev->identifier != (u32)wpan_dev_id)
137                                         continue;
138                                 found = true;
139                                 break;
140                         }
141
142                         if (!found)
143                                 tmp = NULL;
144
145                         if (rdev && tmp != rdev)
146                                 return ERR_PTR(-EINVAL);
147                         rdev = tmp;
148                 }
149         }
150
151         if (attrs[NL802154_ATTR_IFINDEX]) {
152                 int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
153
154                 netdev = __dev_get_by_index(netns, ifindex);
155                 if (netdev) {
156                         if (netdev->ieee802154_ptr)
157                                 tmp = wpan_phy_to_rdev(
158                                                 netdev->ieee802154_ptr->wpan_phy);
159                         else
160                                 tmp = NULL;
161
162                         /* not wireless device -- return error */
163                         if (!tmp)
164                                 return ERR_PTR(-EINVAL);
165
166                         /* mismatch -- return error */
167                         if (rdev && tmp != rdev)
168                                 return ERR_PTR(-EINVAL);
169
170                         rdev = tmp;
171                 }
172         }
173
174         if (!rdev)
175                 return ERR_PTR(-ENODEV);
176
177         /* TODO netns compare */
178
179         return rdev;
180 }
181
182 /* This function returns a pointer to the driver
183  * that the genl_info item that is passed refers to.
184  *
185  * The result of this can be a PTR_ERR and hence must
186  * be checked with IS_ERR() for errors.
187  */
188 static struct cfg802154_registered_device *
189 cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
190 {
191         return __cfg802154_rdev_from_attrs(netns, info->attrs);
192 }
193
194 /* policy for the attributes */
195 static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
196 };
197
198 /* message building helper */
199 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
200                                     int flags, u8 cmd)
201 {
202         /* since there is no private header just add the generic one */
203         return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
204 }
205
206 #define NL802154_FLAG_NEED_WPAN_PHY     0x01
207 #define NL802154_FLAG_NEED_NETDEV       0x02
208 #define NL802154_FLAG_NEED_RTNL         0x04
209 #define NL802154_FLAG_CHECK_NETDEV_UP   0x08
210 #define NL802154_FLAG_NEED_NETDEV_UP    (NL802154_FLAG_NEED_NETDEV |\
211                                          NL802154_FLAG_CHECK_NETDEV_UP)
212 #define NL802154_FLAG_NEED_WPAN_DEV     0x10
213 #define NL802154_FLAG_NEED_WPAN_DEV_UP  (NL802154_FLAG_NEED_WPAN_DEV |\
214                                          NL802154_FLAG_CHECK_NETDEV_UP)
215
216 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
217                              struct genl_info *info)
218 {
219         struct cfg802154_registered_device *rdev;
220         struct wpan_dev *wpan_dev;
221         struct net_device *dev;
222         bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
223
224         if (rtnl)
225                 rtnl_lock();
226
227         if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
228                 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
229                 if (IS_ERR(rdev)) {
230                         if (rtnl)
231                                 rtnl_unlock();
232                         return PTR_ERR(rdev);
233                 }
234                 info->user_ptr[0] = rdev;
235         } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
236                    ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
237                 ASSERT_RTNL();
238                 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
239                                                            info->attrs);
240                 if (IS_ERR(wpan_dev)) {
241                         if (rtnl)
242                                 rtnl_unlock();
243                         return PTR_ERR(wpan_dev);
244                 }
245
246                 dev = wpan_dev->netdev;
247                 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
248
249                 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
250                         if (!dev) {
251                                 if (rtnl)
252                                         rtnl_unlock();
253                                 return -EINVAL;
254                         }
255
256                         info->user_ptr[1] = dev;
257                 } else {
258                         info->user_ptr[1] = wpan_dev;
259                 }
260
261                 if (dev) {
262                         if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
263                             !netif_running(dev)) {
264                                 if (rtnl)
265                                         rtnl_unlock();
266                                 return -ENETDOWN;
267                         }
268
269                         dev_hold(dev);
270                 }
271
272                 info->user_ptr[0] = rdev;
273         }
274
275         return 0;
276 }
277
278 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
279                                struct genl_info *info)
280 {
281         if (info->user_ptr[1]) {
282                 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
283                         struct wpan_dev *wpan_dev = info->user_ptr[1];
284
285                         if (wpan_dev->netdev)
286                                 dev_put(wpan_dev->netdev);
287                 } else {
288                         dev_put(info->user_ptr[1]);
289                 }
290         }
291
292         if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
293                 rtnl_unlock();
294 }
295
296 static const struct genl_ops nl802154_ops[] = {
297 };
298
299 /* initialisation/exit functions */
300 int nl802154_init(void)
301 {
302         return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
303                                                     nl802154_mcgrps);
304 }
305
306 void nl802154_exit(void)
307 {
308         genl_unregister_family(&nl802154_fam);
309 }