blob: a9c8e3e983872faa5c6c03d58ef858cdea07936a [file] [log] [blame]
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +04001/*
2 * Netlink inteface for IEEE 802.15.4 stack
3 *
4 * Copyright 2007, 2008 Siemens AG
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040015 * Written by:
16 * Sergey Lapin <slapin@ossfans.org>
17 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
18 * Maxim Osipov <maxim.osipov@siemens.com>
19 */
20
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/gfp.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040022#include <linux/kernel.h>
23#include <linux/if_arp.h>
24#include <linux/netdevice.h>
25#include <net/netlink.h>
26#include <net/genetlink.h>
27#include <net/sock.h>
28#include <linux/nl802154.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040029#include <linux/export.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040030#include <net/af_ieee802154.h>
31#include <net/nl802154.h>
32#include <net/ieee802154.h>
33#include <net/ieee802154_netdev.h>
Alexander Aring5ad60d32014-10-25 09:41:02 +020034#include <net/cfg802154.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040035
36#include "ieee802154.h"
37
Phoebe Buckheisterae531b92014-03-14 21:24:02 +010038static int nla_put_hwaddr(struct sk_buff *msg, int type, __le64 hwaddr)
39{
40 return nla_put_u64(msg, type, swab64((__force u64)hwaddr));
41}
42
43static __le64 nla_get_hwaddr(const struct nlattr *nla)
44{
45 return ieee802154_devaddr_from_raw(nla_data(nla));
46}
47
48static int nla_put_shortaddr(struct sk_buff *msg, int type, __le16 addr)
49{
50 return nla_put_u16(msg, type, le16_to_cpu(addr));
51}
52
53static __le16 nla_get_shortaddr(const struct nlattr *nla)
54{
55 return cpu_to_le16(nla_get_u16(nla));
56}
57
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040058int ieee802154_nl_assoc_indic(struct net_device *dev,
Varka Bhadram4710d802014-07-02 09:01:09 +053059 struct ieee802154_addr *addr,
60 u8 cap)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040061{
62 struct sk_buff *msg;
63
64 pr_debug("%s\n", __func__);
65
Phoebe Buckheisterae531b92014-03-14 21:24:02 +010066 if (addr->mode != IEEE802154_ADDR_LONG) {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040067 pr_err("%s: received non-long source address!\n", __func__);
68 return -EINVAL;
69 }
70
71 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
72 if (!msg)
73 return -ENOBUFS;
74
David S. Millerbe51da02012-04-01 20:45:25 -040075 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
76 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
77 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
78 dev->dev_addr) ||
Phoebe Buckheisterae531b92014-03-14 21:24:02 +010079 nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
80 addr->extended_addr) ||
David S. Millerbe51da02012-04-01 20:45:25 -040081 nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
82 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040083
Johannes Berg2a94fe42013-11-19 15:19:39 +010084 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040085
86nla_put_failure:
87 nlmsg_free(msg);
88 return -ENOBUFS;
89}
90EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
91
Phoebe Buckheisterb70ab2e2014-03-14 21:23:59 +010092int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
Varka Bhadram4710d802014-07-02 09:01:09 +053093 u8 status)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040094{
95 struct sk_buff *msg;
96
97 pr_debug("%s\n", __func__);
98
99 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
100 if (!msg)
101 return -ENOBUFS;
102
David S. Millerbe51da02012-04-01 20:45:25 -0400103 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
104 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
105 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
106 dev->dev_addr) ||
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100107 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
David S. Millerbe51da02012-04-01 20:45:25 -0400108 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
109 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100110 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400111
112nla_put_failure:
113 nlmsg_free(msg);
114 return -ENOBUFS;
115}
116EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
117
118int ieee802154_nl_disassoc_indic(struct net_device *dev,
Varka Bhadram4710d802014-07-02 09:01:09 +0530119 struct ieee802154_addr *addr,
120 u8 reason)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400121{
122 struct sk_buff *msg;
123
124 pr_debug("%s\n", __func__);
125
126 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
127 if (!msg)
128 return -ENOBUFS;
129
David S. Millerbe51da02012-04-01 20:45:25 -0400130 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
131 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
132 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
133 dev->dev_addr))
134 goto nla_put_failure;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100135 if (addr->mode == IEEE802154_ADDR_LONG) {
136 if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
137 addr->extended_addr))
David S. Millerbe51da02012-04-01 20:45:25 -0400138 goto nla_put_failure;
139 } else {
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100140 if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
141 addr->short_addr))
David S. Millerbe51da02012-04-01 20:45:25 -0400142 goto nla_put_failure;
143 }
144 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
145 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100146 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400147
148nla_put_failure:
149 nlmsg_free(msg);
150 return -ENOBUFS;
151}
152EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
153
154int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
155{
156 struct sk_buff *msg;
157
158 pr_debug("%s\n", __func__);
159
160 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
161 if (!msg)
162 return -ENOBUFS;
163
David S. Millerbe51da02012-04-01 20:45:25 -0400164 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
165 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
166 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
167 dev->dev_addr) ||
168 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
169 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100170 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400171
172nla_put_failure:
173 nlmsg_free(msg);
174 return -ENOBUFS;
175}
176EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
177
Phoebe Buckheisterb70ab2e2014-03-14 21:23:59 +0100178int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
179 __le16 coord_addr)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400180{
181 struct sk_buff *msg;
182
183 pr_debug("%s\n", __func__);
184
185 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
186 if (!msg)
187 return -ENOBUFS;
188
David S. Millerbe51da02012-04-01 20:45:25 -0400189 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
190 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
191 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
192 dev->dev_addr) ||
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100193 nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
194 coord_addr) ||
195 nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
David S. Millerbe51da02012-04-01 20:45:25 -0400196 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100197 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400198
199nla_put_failure:
200 nlmsg_free(msg);
201 return -ENOBUFS;
202}
203EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
204
205int ieee802154_nl_scan_confirm(struct net_device *dev,
Varka Bhadram4710d802014-07-02 09:01:09 +0530206 u8 status, u8 scan_type,
207 u32 unscanned, u8 page,
208 u8 *edl/* , struct list_head *pan_desc_list */)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400209{
210 struct sk_buff *msg;
211
212 pr_debug("%s\n", __func__);
213
214 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
215 if (!msg)
216 return -ENOBUFS;
217
David S. Millerbe51da02012-04-01 20:45:25 -0400218 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
219 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
220 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
221 dev->dev_addr) ||
222 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
223 nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
224 nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
225 nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
226 (edl &&
227 nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
228 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100229 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400230
231nla_put_failure:
232 nlmsg_free(msg);
233 return -ENOBUFS;
234}
235EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
236
237int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
238{
239 struct sk_buff *msg;
240
241 pr_debug("%s\n", __func__);
242
243 msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
244 if (!msg)
245 return -ENOBUFS;
246
David S. Millerbe51da02012-04-01 20:45:25 -0400247 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
248 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
249 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
250 dev->dev_addr) ||
251 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
252 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100253 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400254
255nla_put_failure:
256 nlmsg_free(msg);
257 return -ENOBUFS;
258}
259EXPORT_SYMBOL(ieee802154_nl_start_confirm);
260
Eric W. Biederman15e47302012-09-07 20:12:54 +0000261static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
Varka Bhadram4710d802014-07-02 09:01:09 +0530262 u32 seq, int flags, struct net_device *dev)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400263{
264 void *hdr;
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300265 struct wpan_phy *phy;
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200266 struct ieee802154_mlme_ops *ops;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100267 __le16 short_addr, pan_id;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400268
269 pr_debug("%s\n", __func__);
270
271 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
Varka Bhadram4710d802014-07-02 09:01:09 +0530272 IEEE802154_LIST_IFACE);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400273 if (!hdr)
274 goto out;
275
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200276 ops = ieee802154_mlme_ops(dev);
277 phy = ops->get_phy(dev);
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300278 BUG_ON(!phy);
279
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200280 short_addr = ops->get_short_addr(dev);
281 pan_id = ops->get_pan_id(dev);
Phoebe Buckheisterb70ab2e2014-03-14 21:23:59 +0100282
David S. Millerbe51da02012-04-01 20:45:25 -0400283 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
284 nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
285 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
286 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
287 dev->dev_addr) ||
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100288 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
289 nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id))
David S. Millerbe51da02012-04-01 20:45:25 -0400290 goto nla_put_failure;
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200291
292 if (ops->get_mac_params) {
293 struct ieee802154_mac_params params;
294
295 ops->get_mac_params(dev, &params);
296
297 if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
298 params.transmit_power) ||
299 nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) ||
300 nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE,
301 params.cca_mode) ||
302 nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL,
303 params.cca_ed_level) ||
304 nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES,
305 params.csma_retries) ||
306 nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE,
307 params.min_be) ||
308 nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE,
309 params.max_be) ||
310 nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES,
311 params.frame_retries))
312 goto nla_put_failure;
313 }
314
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300315 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400316 return genlmsg_end(msg, hdr);
317
318nla_put_failure:
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300319 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400320 genlmsg_cancel(msg, hdr);
321out:
322 return -EMSGSIZE;
323}
324
325/* Requests from userspace */
326static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
327{
328 struct net_device *dev;
329
330 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
331 char name[IFNAMSIZ + 1];
Varka Bhadram4710d802014-07-02 09:01:09 +0530332
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400333 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
Varka Bhadram4710d802014-07-02 09:01:09 +0530334 sizeof(name));
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400335 dev = dev_get_by_name(&init_net, name);
Varka Bhadram4710d802014-07-02 09:01:09 +0530336 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400337 dev = dev_get_by_index(&init_net,
338 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
Varka Bhadram4710d802014-07-02 09:01:09 +0530339 } else {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400340 return NULL;
Varka Bhadram4710d802014-07-02 09:01:09 +0530341 }
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400342
343 if (!dev)
344 return NULL;
345
346 if (dev->type != ARPHRD_IEEE802154) {
347 dev_put(dev);
348 return NULL;
349 }
350
351 return dev;
352}
353
Johannes Berg1c582d92013-11-14 17:14:41 +0100354int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400355{
356 struct net_device *dev;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100357 struct ieee802154_addr addr;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400358 u8 page;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000359 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400360
361 if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
362 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
363 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
364 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
365 !info->attrs[IEEE802154_ATTR_CAPABILITY])
366 return -EINVAL;
367
368 dev = ieee802154_nl_get_dev(info);
369 if (!dev)
370 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000371 if (!ieee802154_mlme_ops(dev)->assoc_req)
372 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400373
374 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100375 addr.mode = IEEE802154_ADDR_LONG;
376 addr.extended_addr = nla_get_hwaddr(
377 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400378 } else {
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100379 addr.mode = IEEE802154_ADDR_SHORT;
380 addr.short_addr = nla_get_shortaddr(
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400381 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
382 }
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100383 addr.pan_id = nla_get_shortaddr(
384 info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400385
386 if (info->attrs[IEEE802154_ATTR_PAGE])
387 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
388 else
389 page = 0;
390
391 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
392 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
393 page,
394 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
395
Werner Almesberger56aa0912013-04-04 06:32:35 +0000396out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400397 dev_put(dev);
398 return ret;
399}
400
Johannes Berg1c582d92013-11-14 17:14:41 +0100401int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400402{
403 struct net_device *dev;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100404 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000405 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400406
407 if (!info->attrs[IEEE802154_ATTR_STATUS] ||
408 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
409 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
410 return -EINVAL;
411
412 dev = ieee802154_nl_get_dev(info);
413 if (!dev)
414 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000415 if (!ieee802154_mlme_ops(dev)->assoc_resp)
416 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400417
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100418 addr.mode = IEEE802154_ADDR_LONG;
419 addr.extended_addr = nla_get_hwaddr(
420 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
421 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400422
423 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100424 nla_get_shortaddr(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400425 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
426
Werner Almesberger56aa0912013-04-04 06:32:35 +0000427out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400428 dev_put(dev);
429 return ret;
430}
431
Johannes Berg1c582d92013-11-14 17:14:41 +0100432int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400433{
434 struct net_device *dev;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100435 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000436 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400437
438 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
Varka Bhadram4710d802014-07-02 09:01:09 +0530439 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400440 !info->attrs[IEEE802154_ATTR_REASON])
441 return -EINVAL;
442
443 dev = ieee802154_nl_get_dev(info);
444 if (!dev)
445 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000446 if (!ieee802154_mlme_ops(dev)->disassoc_req)
447 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400448
449 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100450 addr.mode = IEEE802154_ADDR_LONG;
451 addr.extended_addr = nla_get_hwaddr(
452 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400453 } else {
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100454 addr.mode = IEEE802154_ADDR_SHORT;
455 addr.short_addr = nla_get_shortaddr(
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400456 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
457 }
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100458 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400459
460 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
461 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
462
Werner Almesberger56aa0912013-04-04 06:32:35 +0000463out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400464 dev_put(dev);
465 return ret;
466}
467
Varka Bhadram4710d802014-07-02 09:01:09 +0530468/* PANid, channel, beacon_order = 15, superframe_order = 15,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400469 * PAN_coordinator, battery_life_extension = 0,
470 * coord_realignment = 0, security_enable = 0
471*/
Johannes Berg1c582d92013-11-14 17:14:41 +0100472int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400473{
474 struct net_device *dev;
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100475 struct ieee802154_addr addr;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400476
477 u8 channel, bcn_ord, sf_ord;
478 u8 page;
479 int pan_coord, blx, coord_realign;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000480 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400481
482 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
483 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
484 !info->attrs[IEEE802154_ATTR_CHANNEL] ||
485 !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
486 !info->attrs[IEEE802154_ATTR_SF_ORD] ||
487 !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
488 !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
489 !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
490 )
491 return -EINVAL;
492
493 dev = ieee802154_nl_get_dev(info);
494 if (!dev)
495 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000496 if (!ieee802154_mlme_ops(dev)->start_req)
497 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400498
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100499 addr.mode = IEEE802154_ADDR_SHORT;
500 addr.short_addr = nla_get_shortaddr(
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400501 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100502 addr.pan_id = nla_get_shortaddr(
503 info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400504
505 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
506 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
507 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
508 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
509 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
510 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
511
512 if (info->attrs[IEEE802154_ATTR_PAGE])
513 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
514 else
515 page = 0;
516
517
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100518 if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400519 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
520 dev_put(dev);
521 return -EINVAL;
522 }
523
524 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
525 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
526
Werner Almesberger56aa0912013-04-04 06:32:35 +0000527out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400528 dev_put(dev);
529 return ret;
530}
531
Johannes Berg1c582d92013-11-14 17:14:41 +0100532int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400533{
534 struct net_device *dev;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000535 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400536 u8 type;
537 u32 channels;
538 u8 duration;
539 u8 page;
540
541 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
542 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
543 !info->attrs[IEEE802154_ATTR_DURATION])
544 return -EINVAL;
545
546 dev = ieee802154_nl_get_dev(info);
547 if (!dev)
548 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000549 if (!ieee802154_mlme_ops(dev)->scan_req)
550 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400551
552 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
553 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
554 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
555
556 if (info->attrs[IEEE802154_ATTR_PAGE])
557 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
558 else
559 page = 0;
560
561
Varka Bhadram4710d802014-07-02 09:01:09 +0530562 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
563 page, duration);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400564
Werner Almesberger56aa0912013-04-04 06:32:35 +0000565out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400566 dev_put(dev);
567 return ret;
568}
569
Johannes Berg1c582d92013-11-14 17:14:41 +0100570int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400571{
572 /* Request for interface name, index, type, IEEE address,
Varka Bhadram4710d802014-07-02 09:01:09 +0530573 * PAN Id, short address
574 */
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400575 struct sk_buff *msg;
576 struct net_device *dev = NULL;
577 int rc = -ENOBUFS;
578
579 pr_debug("%s\n", __func__);
580
581 dev = ieee802154_nl_get_dev(info);
582 if (!dev)
583 return -ENODEV;
584
Thomas Graf58050fc2012-06-28 03:57:45 +0000585 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400586 if (!msg)
587 goto out_dev;
588
Eric W. Biederman15e47302012-09-07 20:12:54 +0000589 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Varka Bhadram4710d802014-07-02 09:01:09 +0530590 0, dev);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400591 if (rc < 0)
592 goto out_free;
593
594 dev_put(dev);
595
596 return genlmsg_reply(msg, info);
597out_free:
598 nlmsg_free(msg);
599out_dev:
600 dev_put(dev);
601 return rc;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400602}
603
Johannes Berg1c582d92013-11-14 17:14:41 +0100604int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400605{
606 struct net *net = sock_net(skb->sk);
607 struct net_device *dev;
608 int idx;
609 int s_idx = cb->args[0];
610
611 pr_debug("%s\n", __func__);
612
613 idx = 0;
614 for_each_netdev(net, dev) {
615 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
616 goto cont;
617
Eric W. Biederman15e47302012-09-07 20:12:54 +0000618 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Varka Bhadram4710d802014-07-02 09:01:09 +0530619 cb->nlh->nlmsg_seq,
620 NLM_F_MULTI, dev) < 0)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400621 break;
622cont:
623 idx++;
624 }
625 cb->args[0] = idx;
626
627 return skb->len;
628}
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200629
630int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
631{
632 struct net_device *dev = NULL;
633 struct ieee802154_mlme_ops *ops;
634 struct ieee802154_mac_params params;
635 struct wpan_phy *phy;
636 int rc = -EINVAL;
637
638 pr_debug("%s\n", __func__);
639
640 dev = ieee802154_nl_get_dev(info);
641 if (!dev)
642 return -ENODEV;
643
644 ops = ieee802154_mlme_ops(dev);
645
646 if (!ops->get_mac_params || !ops->set_mac_params) {
647 rc = -EOPNOTSUPP;
648 goto out;
649 }
650
651 if (netif_running(dev)) {
652 rc = -EBUSY;
653 goto out;
654 }
655
656 if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
657 !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
658 !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] &&
659 !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] &&
660 !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] &&
661 !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] &&
662 !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
663 goto out;
664
665 phy = ops->get_phy(dev);
666
667 if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
668 (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
669 (!phy->set_cca_ed_level &&
670 info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
671 (!phy->set_csma_params &&
672 (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
673 info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
674 info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
675 (!phy->set_frame_retries &&
676 info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
677 rc = -EOPNOTSUPP;
678 goto out_phy;
679 }
680
681 ops->get_mac_params(dev, &params);
682
683 if (info->attrs[IEEE802154_ATTR_TXPOWER])
684 params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
685
686 if (info->attrs[IEEE802154_ATTR_LBT_ENABLED])
687 params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
688
689 if (info->attrs[IEEE802154_ATTR_CCA_MODE])
690 params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
691
692 if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
693 params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
694
695 if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
696 params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
697
698 if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
699 params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
700
701 if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
702 params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
703
704 if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
705 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
706
707 rc = ops->set_mac_params(dev, &params);
708
709 wpan_phy_put(phy);
710 dev_put(dev);
711 return rc;
712
713out_phy:
714 wpan_phy_put(phy);
715out:
716 dev_put(dev);
717 return rc;
718}
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200719
720
721
722static int
723ieee802154_llsec_parse_key_id(struct genl_info *info,
724 struct ieee802154_llsec_key_id *desc)
725{
726 memset(desc, 0, sizeof(*desc));
727
728 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE])
729 return -EINVAL;
730
731 desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]);
732
733 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
734 if (!info->attrs[IEEE802154_ATTR_PAN_ID] &&
735 !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] ||
736 info->attrs[IEEE802154_ATTR_HW_ADDR]))
737 return -EINVAL;
738
739 desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
740
741 if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) {
742 desc->device_addr.mode = IEEE802154_ADDR_SHORT;
743 desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
744 } else {
745 desc->device_addr.mode = IEEE802154_ADDR_LONG;
746 desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
747 }
748 }
749
750 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
751 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID])
752 return -EINVAL;
753
754 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
755 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT])
756 return -EINVAL;
757
758 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
759 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED])
760 return -EINVAL;
761
762 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT)
763 desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]);
764
765 switch (desc->mode) {
766 case IEEE802154_SCF_KEY_SHORT_INDEX:
767 {
768 u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]);
Varka Bhadram4710d802014-07-02 09:01:09 +0530769
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200770 desc->short_source = cpu_to_le32(source);
771 break;
772 }
773 case IEEE802154_SCF_KEY_HW_INDEX:
774 desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]);
775 break;
776 }
777
778 return 0;
779}
780
781static int
782ieee802154_llsec_fill_key_id(struct sk_buff *msg,
783 const struct ieee802154_llsec_key_id *desc)
784{
785 if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode))
786 return -EMSGSIZE;
787
788 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
789 if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID,
790 desc->device_addr.pan_id))
791 return -EMSGSIZE;
792
793 if (desc->device_addr.mode == IEEE802154_ADDR_SHORT &&
794 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
795 desc->device_addr.short_addr))
796 return -EMSGSIZE;
797
798 if (desc->device_addr.mode == IEEE802154_ADDR_LONG &&
799 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR,
800 desc->device_addr.extended_addr))
801 return -EMSGSIZE;
802 }
803
804 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
805 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id))
806 return -EMSGSIZE;
807
808 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
809 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
810 le32_to_cpu(desc->short_source)))
811 return -EMSGSIZE;
812
813 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
814 nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
815 desc->extended_source))
816 return -EMSGSIZE;
817
818 return 0;
819}
820
821int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info)
822{
823 struct sk_buff *msg;
824 struct net_device *dev = NULL;
825 int rc = -ENOBUFS;
826 struct ieee802154_mlme_ops *ops;
827 void *hdr;
828 struct ieee802154_llsec_params params;
829
830 pr_debug("%s\n", __func__);
831
832 dev = ieee802154_nl_get_dev(info);
833 if (!dev)
834 return -ENODEV;
835
836 ops = ieee802154_mlme_ops(dev);
Dan Carpenterb3f7a7b2014-05-22 10:53:06 +0300837 if (!ops->llsec) {
838 rc = -EOPNOTSUPP;
839 goto out_dev;
840 }
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200841
842 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
843 if (!msg)
844 goto out_dev;
845
846 hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0,
Varka Bhadram4710d802014-07-02 09:01:09 +0530847 IEEE802154_LLSEC_GETPARAMS);
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200848 if (!hdr)
849 goto out_free;
850
851 rc = ops->llsec->get_params(dev, &params);
852 if (rc < 0)
853 goto out_free;
854
855 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
856 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
857 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) ||
858 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) ||
859 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
860 be32_to_cpu(params.frame_counter)) ||
861 ieee802154_llsec_fill_key_id(msg, &params.out_key))
862 goto out_free;
863
864 dev_put(dev);
865
866 return ieee802154_nl_reply(msg, info);
867out_free:
868 nlmsg_free(msg);
869out_dev:
870 dev_put(dev);
871 return rc;
872}
873
874int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info)
875{
876 struct net_device *dev = NULL;
877 int rc = -EINVAL;
878 struct ieee802154_mlme_ops *ops;
879 struct ieee802154_llsec_params params;
880 int changed = 0;
881
882 pr_debug("%s\n", __func__);
883
884 dev = ieee802154_nl_get_dev(info);
885 if (!dev)
886 return -ENODEV;
887
888 if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] &&
889 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] &&
890 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL])
891 goto out;
892
893 ops = ieee802154_mlme_ops(dev);
894 if (!ops->llsec) {
895 rc = -EOPNOTSUPP;
896 goto out;
897 }
898
899 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] &&
900 nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7)
901 goto out;
902
903 if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) {
904 params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]);
905 changed |= IEEE802154_LLSEC_PARAM_ENABLED;
906 }
907
908 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) {
909 if (ieee802154_llsec_parse_key_id(info, &params.out_key))
910 goto out;
911
912 changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
913 }
914
915 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) {
916 params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]);
917 changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
918 }
919
920 if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) {
921 u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
922
923 params.frame_counter = cpu_to_be32(fc);
924 changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
925 }
926
927 rc = ops->llsec->set_params(dev, &params, changed);
928
929 dev_put(dev);
930
931 return rc;
932out:
933 dev_put(dev);
934 return rc;
935}
936
937
938
939struct llsec_dump_data {
940 struct sk_buff *skb;
941 int s_idx, s_idx2;
942 int portid;
943 int nlmsg_seq;
944 struct net_device *dev;
945 struct ieee802154_mlme_ops *ops;
946 struct ieee802154_llsec_table *table;
947};
948
949static int
950ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
Varka Bhadram4710d802014-07-02 09:01:09 +0530951 int (*step)(struct llsec_dump_data *))
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200952{
953 struct net *net = sock_net(skb->sk);
954 struct net_device *dev;
955 struct llsec_dump_data data;
956 int idx = 0;
957 int first_dev = cb->args[0];
958 int rc;
959
960 for_each_netdev(net, dev) {
961 if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
962 goto skip;
963
964 data.ops = ieee802154_mlme_ops(dev);
965 if (!data.ops->llsec)
966 goto skip;
967
968 data.skb = skb;
969 data.s_idx = cb->args[1];
970 data.s_idx2 = cb->args[2];
971 data.dev = dev;
972 data.portid = NETLINK_CB(cb->skb).portid;
973 data.nlmsg_seq = cb->nlh->nlmsg_seq;
974
975 data.ops->llsec->lock_table(dev);
976 data.ops->llsec->get_table(data.dev, &data.table);
977 rc = step(&data);
978 data.ops->llsec->unlock_table(dev);
979
980 if (rc < 0)
981 break;
982
983skip:
984 idx++;
985 }
986 cb->args[0] = idx;
987
988 return skb->len;
989}
990
991static int
992ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
993 int (*fn)(struct net_device*, struct genl_info*))
994{
995 struct net_device *dev = NULL;
996 int rc = -EINVAL;
997
998 dev = ieee802154_nl_get_dev(info);
999 if (!dev)
1000 return -ENODEV;
1001
1002 if (!ieee802154_mlme_ops(dev)->llsec)
1003 rc = -EOPNOTSUPP;
1004 else
1005 rc = fn(dev, info);
1006
1007 dev_put(dev);
1008 return rc;
1009}
1010
1011
1012
1013static int
1014ieee802154_llsec_parse_key(struct genl_info *info,
1015 struct ieee802154_llsec_key *key)
1016{
1017 u8 frames;
1018 u32 commands[256 / 32];
1019
1020 memset(key, 0, sizeof(*key));
1021
1022 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] ||
1023 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES])
1024 return -EINVAL;
1025
1026 frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]);
1027 if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) &&
1028 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS])
1029 return -EINVAL;
1030
1031 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) {
1032 nla_memcpy(commands,
1033 info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS],
1034 256 / 8);
1035
1036 if (commands[0] || commands[1] || commands[2] || commands[3] ||
1037 commands[4] || commands[5] || commands[6] ||
1038 commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1))
1039 return -EINVAL;
1040
1041 key->cmd_frame_ids = commands[7];
1042 }
1043
1044 key->frame_types = frames;
1045
1046 nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES],
1047 IEEE802154_LLSEC_KEY_SIZE);
1048
1049 return 0;
1050}
1051
1052static int llsec_add_key(struct net_device *dev, struct genl_info *info)
1053{
1054 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1055 struct ieee802154_llsec_key key;
1056 struct ieee802154_llsec_key_id id;
1057
1058 if (ieee802154_llsec_parse_key(info, &key) ||
1059 ieee802154_llsec_parse_key_id(info, &id))
1060 return -EINVAL;
1061
1062 return ops->llsec->add_key(dev, &id, &key);
1063}
1064
1065int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info)
1066{
1067 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1068 (NLM_F_CREATE | NLM_F_EXCL))
1069 return -EINVAL;
1070
1071 return ieee802154_nl_llsec_change(skb, info, llsec_add_key);
1072}
1073
1074static int llsec_remove_key(struct net_device *dev, struct genl_info *info)
1075{
1076 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1077 struct ieee802154_llsec_key_id id;
1078
1079 if (ieee802154_llsec_parse_key_id(info, &id))
1080 return -EINVAL;
1081
1082 return ops->llsec->del_key(dev, &id);
1083}
1084
1085int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info)
1086{
1087 return ieee802154_nl_llsec_change(skb, info, llsec_remove_key);
1088}
1089
1090static int
1091ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq,
1092 const struct ieee802154_llsec_key_entry *key,
1093 const struct net_device *dev)
1094{
1095 void *hdr;
1096 u32 commands[256 / 32];
1097
1098 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1099 IEEE802154_LLSEC_LIST_KEY);
1100 if (!hdr)
1101 goto out;
1102
1103 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1104 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1105 ieee802154_llsec_fill_key_id(msg, &key->id) ||
1106 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
1107 key->key->frame_types))
1108 goto nla_put_failure;
1109
1110 if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) {
1111 memset(commands, 0, sizeof(commands));
1112 commands[7] = key->key->cmd_frame_ids;
1113 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
1114 sizeof(commands), commands))
1115 goto nla_put_failure;
1116 }
1117
1118 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES,
1119 IEEE802154_LLSEC_KEY_SIZE, key->key->key))
1120 goto nla_put_failure;
1121
1122 genlmsg_end(msg, hdr);
1123 return 0;
1124
1125nla_put_failure:
1126 genlmsg_cancel(msg, hdr);
1127out:
1128 return -EMSGSIZE;
1129}
1130
1131static int llsec_iter_keys(struct llsec_dump_data *data)
1132{
1133 struct ieee802154_llsec_key_entry *pos;
1134 int rc = 0, idx = 0;
1135
1136 list_for_each_entry(pos, &data->table->keys, list) {
1137 if (idx++ < data->s_idx)
1138 continue;
1139
1140 if (ieee802154_nl_fill_key(data->skb, data->portid,
1141 data->nlmsg_seq, pos, data->dev)) {
1142 rc = -EMSGSIZE;
1143 break;
1144 }
1145
1146 data->s_idx++;
1147 }
1148
1149 return rc;
1150}
1151
1152int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
1153{
1154 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
1155}
1156
1157
1158
1159static int
1160llsec_parse_dev(struct genl_info *info,
1161 struct ieee802154_llsec_device *dev)
1162{
1163 memset(dev, 0, sizeof(*dev));
1164
1165 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1166 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1167 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] ||
1168 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] ||
1169 (!!info->attrs[IEEE802154_ATTR_PAN_ID] !=
1170 !!info->attrs[IEEE802154_ATTR_SHORT_ADDR]))
1171 return -EINVAL;
1172
1173 if (info->attrs[IEEE802154_ATTR_PAN_ID]) {
1174 dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
1175 dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
1176 } else {
1177 dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF);
1178 }
1179
1180 dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1181 dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1182 dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1183 dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]);
1184
1185 if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX)
1186 return -EINVAL;
1187
1188 return 0;
1189}
1190
1191static int llsec_add_dev(struct net_device *dev, struct genl_info *info)
1192{
1193 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1194 struct ieee802154_llsec_device desc;
1195
1196 if (llsec_parse_dev(info, &desc))
1197 return -EINVAL;
1198
1199 return ops->llsec->add_dev(dev, &desc);
1200}
1201
1202int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info)
1203{
1204 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1205 (NLM_F_CREATE | NLM_F_EXCL))
1206 return -EINVAL;
1207
1208 return ieee802154_nl_llsec_change(skb, info, llsec_add_dev);
1209}
1210
1211static int llsec_del_dev(struct net_device *dev, struct genl_info *info)
1212{
1213 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1214 __le64 devaddr;
1215
1216 if (!info->attrs[IEEE802154_ATTR_HW_ADDR])
1217 return -EINVAL;
1218
1219 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1220
1221 return ops->llsec->del_dev(dev, devaddr);
1222}
1223
1224int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info)
1225{
1226 return ieee802154_nl_llsec_change(skb, info, llsec_del_dev);
1227}
1228
1229static int
1230ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq,
1231 const struct ieee802154_llsec_device *desc,
1232 const struct net_device *dev)
1233{
1234 void *hdr;
1235
1236 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1237 IEEE802154_LLSEC_LIST_DEV);
1238 if (!hdr)
1239 goto out;
1240
1241 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1242 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1243 nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) ||
1244 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
1245 desc->short_addr) ||
1246 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) ||
1247 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1248 desc->frame_counter) ||
1249 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1250 desc->seclevel_exempt) ||
1251 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode))
1252 goto nla_put_failure;
1253
1254 genlmsg_end(msg, hdr);
1255 return 0;
1256
1257nla_put_failure:
1258 genlmsg_cancel(msg, hdr);
1259out:
1260 return -EMSGSIZE;
1261}
1262
1263static int llsec_iter_devs(struct llsec_dump_data *data)
1264{
1265 struct ieee802154_llsec_device *pos;
1266 int rc = 0, idx = 0;
1267
1268 list_for_each_entry(pos, &data->table->devices, list) {
1269 if (idx++ < data->s_idx)
1270 continue;
1271
1272 if (ieee802154_nl_fill_dev(data->skb, data->portid,
1273 data->nlmsg_seq, pos, data->dev)) {
1274 rc = -EMSGSIZE;
1275 break;
1276 }
1277
1278 data->s_idx++;
1279 }
1280
1281 return rc;
1282}
1283
1284int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
1285{
1286 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
1287}
1288
1289
1290
1291static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
1292{
1293 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1294 struct ieee802154_llsec_device_key key;
1295 __le64 devaddr;
1296
1297 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1298 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1299 ieee802154_llsec_parse_key_id(info, &key.key_id))
1300 return -EINVAL;
1301
1302 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1303 key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1304
1305 return ops->llsec->add_devkey(dev, devaddr, &key);
1306}
1307
1308int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info)
1309{
1310 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1311 (NLM_F_CREATE | NLM_F_EXCL))
1312 return -EINVAL;
1313
1314 return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey);
1315}
1316
1317static int llsec_del_devkey(struct net_device *dev, struct genl_info *info)
1318{
1319 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1320 struct ieee802154_llsec_device_key key;
1321 __le64 devaddr;
1322
1323 if (!info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1324 ieee802154_llsec_parse_key_id(info, &key.key_id))
1325 return -EINVAL;
1326
1327 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1328
1329 return ops->llsec->del_devkey(dev, devaddr, &key);
1330}
1331
1332int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info)
1333{
1334 return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey);
1335}
1336
1337static int
1338ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq,
1339 __le64 devaddr,
1340 const struct ieee802154_llsec_device_key *devkey,
1341 const struct net_device *dev)
1342{
1343 void *hdr;
1344
1345 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1346 IEEE802154_LLSEC_LIST_DEVKEY);
1347 if (!hdr)
1348 goto out;
1349
1350 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1351 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1352 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) ||
1353 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1354 devkey->frame_counter) ||
1355 ieee802154_llsec_fill_key_id(msg, &devkey->key_id))
1356 goto nla_put_failure;
1357
1358 genlmsg_end(msg, hdr);
1359 return 0;
1360
1361nla_put_failure:
1362 genlmsg_cancel(msg, hdr);
1363out:
1364 return -EMSGSIZE;
1365}
1366
1367static int llsec_iter_devkeys(struct llsec_dump_data *data)
1368{
1369 struct ieee802154_llsec_device *dpos;
1370 struct ieee802154_llsec_device_key *kpos;
1371 int rc = 0, idx = 0, idx2;
1372
1373 list_for_each_entry(dpos, &data->table->devices, list) {
1374 if (idx++ < data->s_idx)
1375 continue;
1376
1377 idx2 = 0;
1378
1379 list_for_each_entry(kpos, &dpos->keys, list) {
1380 if (idx2++ < data->s_idx2)
1381 continue;
1382
1383 if (ieee802154_nl_fill_devkey(data->skb, data->portid,
1384 data->nlmsg_seq,
1385 dpos->hwaddr, kpos,
1386 data->dev)) {
1387 return rc = -EMSGSIZE;
1388 }
1389
1390 data->s_idx2++;
1391 }
1392
1393 data->s_idx++;
1394 }
1395
1396 return rc;
1397}
1398
1399int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
1400 struct netlink_callback *cb)
1401{
1402 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
1403}
1404
1405
1406
1407static int
1408llsec_parse_seclevel(struct genl_info *info,
1409 struct ieee802154_llsec_seclevel *sl)
1410{
1411 memset(sl, 0, sizeof(*sl));
1412
1413 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] ||
1414 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] ||
1415 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE])
1416 return -EINVAL;
1417
1418 sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]);
1419 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) {
1420 if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID])
1421 return -EINVAL;
1422
1423 sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]);
1424 }
1425
1426 sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]);
1427 sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1428
1429 return 0;
1430}
1431
1432static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info)
1433{
1434 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1435 struct ieee802154_llsec_seclevel sl;
1436
1437 if (llsec_parse_seclevel(info, &sl))
1438 return -EINVAL;
1439
1440 return ops->llsec->add_seclevel(dev, &sl);
1441}
1442
1443int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info)
1444{
1445 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1446 (NLM_F_CREATE | NLM_F_EXCL))
1447 return -EINVAL;
1448
1449 return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel);
1450}
1451
1452static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info)
1453{
1454 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1455 struct ieee802154_llsec_seclevel sl;
1456
1457 if (llsec_parse_seclevel(info, &sl))
1458 return -EINVAL;
1459
1460 return ops->llsec->del_seclevel(dev, &sl);
1461}
1462
1463int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info)
1464{
1465 return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel);
1466}
1467
1468static int
1469ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq,
1470 const struct ieee802154_llsec_seclevel *sl,
1471 const struct net_device *dev)
1472{
1473 void *hdr;
1474
1475 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1476 IEEE802154_LLSEC_LIST_SECLEVEL);
1477 if (!hdr)
1478 goto out;
1479
1480 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1481 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1482 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) ||
1483 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) ||
1484 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1485 sl->device_override))
1486 goto nla_put_failure;
1487
1488 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
1489 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
1490 sl->cmd_frame_id))
1491 goto nla_put_failure;
1492
1493 genlmsg_end(msg, hdr);
1494 return 0;
1495
1496nla_put_failure:
1497 genlmsg_cancel(msg, hdr);
1498out:
1499 return -EMSGSIZE;
1500}
1501
1502static int llsec_iter_seclevels(struct llsec_dump_data *data)
1503{
1504 struct ieee802154_llsec_seclevel *pos;
1505 int rc = 0, idx = 0;
1506
1507 list_for_each_entry(pos, &data->table->security_levels, list) {
1508 if (idx++ < data->s_idx)
1509 continue;
1510
1511 if (ieee802154_nl_fill_seclevel(data->skb, data->portid,
1512 data->nlmsg_seq, pos,
1513 data->dev)) {
1514 rc = -EMSGSIZE;
1515 break;
1516 }
1517
1518 data->s_idx++;
1519 }
1520
1521 return rc;
1522}
1523
1524int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
1525 struct netlink_callback *cb)
1526{
1527 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels);
1528}