blob: abd0f31bdc665d40145340ec343dad06d47f8a43 [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>
Alexander Aring4ca24ac2014-10-25 09:41:04 +020025#include <linux/ieee802154.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040026#include <net/netlink.h>
27#include <net/genetlink.h>
28#include <net/sock.h>
29#include <linux/nl802154.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040030#include <linux/export.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040031#include <net/af_ieee802154.h>
32#include <net/nl802154.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040033#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
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200667 ops->get_mac_params(dev, &params);
668
669 if (info->attrs[IEEE802154_ATTR_TXPOWER])
670 params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
671
672 if (info->attrs[IEEE802154_ATTR_LBT_ENABLED])
673 params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
674
675 if (info->attrs[IEEE802154_ATTR_CCA_MODE])
676 params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
677
678 if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
679 params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
680
681 if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
682 params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
683
684 if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
685 params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
686
687 if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
688 params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
689
690 if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
691 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
692
693 rc = ops->set_mac_params(dev, &params);
694
695 wpan_phy_put(phy);
696 dev_put(dev);
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200697
Alexander Aringa543c592014-10-28 18:21:23 +0100698 return 0;
699
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200700out:
701 dev_put(dev);
702 return rc;
703}
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200704
705
706
707static int
708ieee802154_llsec_parse_key_id(struct genl_info *info,
709 struct ieee802154_llsec_key_id *desc)
710{
711 memset(desc, 0, sizeof(*desc));
712
713 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE])
714 return -EINVAL;
715
716 desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]);
717
718 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
719 if (!info->attrs[IEEE802154_ATTR_PAN_ID] &&
720 !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] ||
721 info->attrs[IEEE802154_ATTR_HW_ADDR]))
722 return -EINVAL;
723
724 desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
725
726 if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) {
727 desc->device_addr.mode = IEEE802154_ADDR_SHORT;
728 desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
729 } else {
730 desc->device_addr.mode = IEEE802154_ADDR_LONG;
731 desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
732 }
733 }
734
735 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
736 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID])
737 return -EINVAL;
738
739 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
740 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT])
741 return -EINVAL;
742
743 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
744 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED])
745 return -EINVAL;
746
747 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT)
748 desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]);
749
750 switch (desc->mode) {
751 case IEEE802154_SCF_KEY_SHORT_INDEX:
752 {
753 u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]);
Varka Bhadram4710d802014-07-02 09:01:09 +0530754
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200755 desc->short_source = cpu_to_le32(source);
756 break;
757 }
758 case IEEE802154_SCF_KEY_HW_INDEX:
759 desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]);
760 break;
761 }
762
763 return 0;
764}
765
766static int
767ieee802154_llsec_fill_key_id(struct sk_buff *msg,
768 const struct ieee802154_llsec_key_id *desc)
769{
770 if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode))
771 return -EMSGSIZE;
772
773 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
774 if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID,
775 desc->device_addr.pan_id))
776 return -EMSGSIZE;
777
778 if (desc->device_addr.mode == IEEE802154_ADDR_SHORT &&
779 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
780 desc->device_addr.short_addr))
781 return -EMSGSIZE;
782
783 if (desc->device_addr.mode == IEEE802154_ADDR_LONG &&
784 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR,
785 desc->device_addr.extended_addr))
786 return -EMSGSIZE;
787 }
788
789 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
790 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id))
791 return -EMSGSIZE;
792
793 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
794 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
795 le32_to_cpu(desc->short_source)))
796 return -EMSGSIZE;
797
798 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
799 nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
800 desc->extended_source))
801 return -EMSGSIZE;
802
803 return 0;
804}
805
806int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info)
807{
808 struct sk_buff *msg;
809 struct net_device *dev = NULL;
810 int rc = -ENOBUFS;
811 struct ieee802154_mlme_ops *ops;
812 void *hdr;
813 struct ieee802154_llsec_params params;
814
815 pr_debug("%s\n", __func__);
816
817 dev = ieee802154_nl_get_dev(info);
818 if (!dev)
819 return -ENODEV;
820
821 ops = ieee802154_mlme_ops(dev);
Dan Carpenterb3f7a7b2014-05-22 10:53:06 +0300822 if (!ops->llsec) {
823 rc = -EOPNOTSUPP;
824 goto out_dev;
825 }
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200826
827 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
828 if (!msg)
829 goto out_dev;
830
831 hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0,
Varka Bhadram4710d802014-07-02 09:01:09 +0530832 IEEE802154_LLSEC_GETPARAMS);
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200833 if (!hdr)
834 goto out_free;
835
836 rc = ops->llsec->get_params(dev, &params);
837 if (rc < 0)
838 goto out_free;
839
840 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
841 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
842 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) ||
843 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) ||
844 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
845 be32_to_cpu(params.frame_counter)) ||
846 ieee802154_llsec_fill_key_id(msg, &params.out_key))
847 goto out_free;
848
849 dev_put(dev);
850
851 return ieee802154_nl_reply(msg, info);
852out_free:
853 nlmsg_free(msg);
854out_dev:
855 dev_put(dev);
856 return rc;
857}
858
859int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info)
860{
861 struct net_device *dev = NULL;
862 int rc = -EINVAL;
863 struct ieee802154_mlme_ops *ops;
864 struct ieee802154_llsec_params params;
865 int changed = 0;
866
867 pr_debug("%s\n", __func__);
868
869 dev = ieee802154_nl_get_dev(info);
870 if (!dev)
871 return -ENODEV;
872
873 if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] &&
874 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] &&
875 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL])
876 goto out;
877
878 ops = ieee802154_mlme_ops(dev);
879 if (!ops->llsec) {
880 rc = -EOPNOTSUPP;
881 goto out;
882 }
883
884 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] &&
885 nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7)
886 goto out;
887
888 if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) {
889 params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]);
890 changed |= IEEE802154_LLSEC_PARAM_ENABLED;
891 }
892
893 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) {
894 if (ieee802154_llsec_parse_key_id(info, &params.out_key))
895 goto out;
896
897 changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
898 }
899
900 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) {
901 params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]);
902 changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
903 }
904
905 if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) {
906 u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
907
908 params.frame_counter = cpu_to_be32(fc);
909 changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
910 }
911
912 rc = ops->llsec->set_params(dev, &params, changed);
913
914 dev_put(dev);
915
916 return rc;
917out:
918 dev_put(dev);
919 return rc;
920}
921
922
923
924struct llsec_dump_data {
925 struct sk_buff *skb;
926 int s_idx, s_idx2;
927 int portid;
928 int nlmsg_seq;
929 struct net_device *dev;
930 struct ieee802154_mlme_ops *ops;
931 struct ieee802154_llsec_table *table;
932};
933
934static int
935ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
Varka Bhadram4710d802014-07-02 09:01:09 +0530936 int (*step)(struct llsec_dump_data *))
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200937{
938 struct net *net = sock_net(skb->sk);
939 struct net_device *dev;
940 struct llsec_dump_data data;
941 int idx = 0;
942 int first_dev = cb->args[0];
943 int rc;
944
945 for_each_netdev(net, dev) {
946 if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
947 goto skip;
948
949 data.ops = ieee802154_mlme_ops(dev);
950 if (!data.ops->llsec)
951 goto skip;
952
953 data.skb = skb;
954 data.s_idx = cb->args[1];
955 data.s_idx2 = cb->args[2];
956 data.dev = dev;
957 data.portid = NETLINK_CB(cb->skb).portid;
958 data.nlmsg_seq = cb->nlh->nlmsg_seq;
959
960 data.ops->llsec->lock_table(dev);
961 data.ops->llsec->get_table(data.dev, &data.table);
962 rc = step(&data);
963 data.ops->llsec->unlock_table(dev);
964
965 if (rc < 0)
966 break;
967
968skip:
969 idx++;
970 }
971 cb->args[0] = idx;
972
973 return skb->len;
974}
975
976static int
977ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
978 int (*fn)(struct net_device*, struct genl_info*))
979{
980 struct net_device *dev = NULL;
981 int rc = -EINVAL;
982
983 dev = ieee802154_nl_get_dev(info);
984 if (!dev)
985 return -ENODEV;
986
987 if (!ieee802154_mlme_ops(dev)->llsec)
988 rc = -EOPNOTSUPP;
989 else
990 rc = fn(dev, info);
991
992 dev_put(dev);
993 return rc;
994}
995
996
997
998static int
999ieee802154_llsec_parse_key(struct genl_info *info,
1000 struct ieee802154_llsec_key *key)
1001{
1002 u8 frames;
1003 u32 commands[256 / 32];
1004
1005 memset(key, 0, sizeof(*key));
1006
1007 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] ||
1008 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES])
1009 return -EINVAL;
1010
1011 frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]);
1012 if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) &&
1013 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS])
1014 return -EINVAL;
1015
1016 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) {
1017 nla_memcpy(commands,
1018 info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS],
1019 256 / 8);
1020
1021 if (commands[0] || commands[1] || commands[2] || commands[3] ||
1022 commands[4] || commands[5] || commands[6] ||
1023 commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1))
1024 return -EINVAL;
1025
1026 key->cmd_frame_ids = commands[7];
1027 }
1028
1029 key->frame_types = frames;
1030
1031 nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES],
1032 IEEE802154_LLSEC_KEY_SIZE);
1033
1034 return 0;
1035}
1036
1037static int llsec_add_key(struct net_device *dev, struct genl_info *info)
1038{
1039 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1040 struct ieee802154_llsec_key key;
1041 struct ieee802154_llsec_key_id id;
1042
1043 if (ieee802154_llsec_parse_key(info, &key) ||
1044 ieee802154_llsec_parse_key_id(info, &id))
1045 return -EINVAL;
1046
1047 return ops->llsec->add_key(dev, &id, &key);
1048}
1049
1050int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info)
1051{
1052 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1053 (NLM_F_CREATE | NLM_F_EXCL))
1054 return -EINVAL;
1055
1056 return ieee802154_nl_llsec_change(skb, info, llsec_add_key);
1057}
1058
1059static int llsec_remove_key(struct net_device *dev, struct genl_info *info)
1060{
1061 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1062 struct ieee802154_llsec_key_id id;
1063
1064 if (ieee802154_llsec_parse_key_id(info, &id))
1065 return -EINVAL;
1066
1067 return ops->llsec->del_key(dev, &id);
1068}
1069
1070int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info)
1071{
1072 return ieee802154_nl_llsec_change(skb, info, llsec_remove_key);
1073}
1074
1075static int
1076ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq,
1077 const struct ieee802154_llsec_key_entry *key,
1078 const struct net_device *dev)
1079{
1080 void *hdr;
1081 u32 commands[256 / 32];
1082
1083 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1084 IEEE802154_LLSEC_LIST_KEY);
1085 if (!hdr)
1086 goto out;
1087
1088 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1089 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1090 ieee802154_llsec_fill_key_id(msg, &key->id) ||
1091 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
1092 key->key->frame_types))
1093 goto nla_put_failure;
1094
1095 if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) {
1096 memset(commands, 0, sizeof(commands));
1097 commands[7] = key->key->cmd_frame_ids;
1098 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
1099 sizeof(commands), commands))
1100 goto nla_put_failure;
1101 }
1102
1103 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES,
1104 IEEE802154_LLSEC_KEY_SIZE, key->key->key))
1105 goto nla_put_failure;
1106
1107 genlmsg_end(msg, hdr);
1108 return 0;
1109
1110nla_put_failure:
1111 genlmsg_cancel(msg, hdr);
1112out:
1113 return -EMSGSIZE;
1114}
1115
1116static int llsec_iter_keys(struct llsec_dump_data *data)
1117{
1118 struct ieee802154_llsec_key_entry *pos;
1119 int rc = 0, idx = 0;
1120
1121 list_for_each_entry(pos, &data->table->keys, list) {
1122 if (idx++ < data->s_idx)
1123 continue;
1124
1125 if (ieee802154_nl_fill_key(data->skb, data->portid,
1126 data->nlmsg_seq, pos, data->dev)) {
1127 rc = -EMSGSIZE;
1128 break;
1129 }
1130
1131 data->s_idx++;
1132 }
1133
1134 return rc;
1135}
1136
1137int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
1138{
1139 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
1140}
1141
1142
1143
1144static int
1145llsec_parse_dev(struct genl_info *info,
1146 struct ieee802154_llsec_device *dev)
1147{
1148 memset(dev, 0, sizeof(*dev));
1149
1150 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1151 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1152 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] ||
1153 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] ||
1154 (!!info->attrs[IEEE802154_ATTR_PAN_ID] !=
1155 !!info->attrs[IEEE802154_ATTR_SHORT_ADDR]))
1156 return -EINVAL;
1157
1158 if (info->attrs[IEEE802154_ATTR_PAN_ID]) {
1159 dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
1160 dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
1161 } else {
1162 dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF);
1163 }
1164
1165 dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1166 dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1167 dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1168 dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]);
1169
1170 if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX)
1171 return -EINVAL;
1172
1173 return 0;
1174}
1175
1176static int llsec_add_dev(struct net_device *dev, struct genl_info *info)
1177{
1178 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1179 struct ieee802154_llsec_device desc;
1180
1181 if (llsec_parse_dev(info, &desc))
1182 return -EINVAL;
1183
1184 return ops->llsec->add_dev(dev, &desc);
1185}
1186
1187int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info)
1188{
1189 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1190 (NLM_F_CREATE | NLM_F_EXCL))
1191 return -EINVAL;
1192
1193 return ieee802154_nl_llsec_change(skb, info, llsec_add_dev);
1194}
1195
1196static int llsec_del_dev(struct net_device *dev, struct genl_info *info)
1197{
1198 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1199 __le64 devaddr;
1200
1201 if (!info->attrs[IEEE802154_ATTR_HW_ADDR])
1202 return -EINVAL;
1203
1204 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1205
1206 return ops->llsec->del_dev(dev, devaddr);
1207}
1208
1209int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info)
1210{
1211 return ieee802154_nl_llsec_change(skb, info, llsec_del_dev);
1212}
1213
1214static int
1215ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq,
1216 const struct ieee802154_llsec_device *desc,
1217 const struct net_device *dev)
1218{
1219 void *hdr;
1220
1221 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1222 IEEE802154_LLSEC_LIST_DEV);
1223 if (!hdr)
1224 goto out;
1225
1226 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1227 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1228 nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) ||
1229 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
1230 desc->short_addr) ||
1231 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) ||
1232 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1233 desc->frame_counter) ||
1234 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1235 desc->seclevel_exempt) ||
1236 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode))
1237 goto nla_put_failure;
1238
1239 genlmsg_end(msg, hdr);
1240 return 0;
1241
1242nla_put_failure:
1243 genlmsg_cancel(msg, hdr);
1244out:
1245 return -EMSGSIZE;
1246}
1247
1248static int llsec_iter_devs(struct llsec_dump_data *data)
1249{
1250 struct ieee802154_llsec_device *pos;
1251 int rc = 0, idx = 0;
1252
1253 list_for_each_entry(pos, &data->table->devices, list) {
1254 if (idx++ < data->s_idx)
1255 continue;
1256
1257 if (ieee802154_nl_fill_dev(data->skb, data->portid,
1258 data->nlmsg_seq, pos, data->dev)) {
1259 rc = -EMSGSIZE;
1260 break;
1261 }
1262
1263 data->s_idx++;
1264 }
1265
1266 return rc;
1267}
1268
1269int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
1270{
1271 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
1272}
1273
1274
1275
1276static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
1277{
1278 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1279 struct ieee802154_llsec_device_key key;
1280 __le64 devaddr;
1281
1282 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1283 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1284 ieee802154_llsec_parse_key_id(info, &key.key_id))
1285 return -EINVAL;
1286
1287 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1288 key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1289
1290 return ops->llsec->add_devkey(dev, devaddr, &key);
1291}
1292
1293int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info)
1294{
1295 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1296 (NLM_F_CREATE | NLM_F_EXCL))
1297 return -EINVAL;
1298
1299 return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey);
1300}
1301
1302static int llsec_del_devkey(struct net_device *dev, struct genl_info *info)
1303{
1304 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1305 struct ieee802154_llsec_device_key key;
1306 __le64 devaddr;
1307
1308 if (!info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1309 ieee802154_llsec_parse_key_id(info, &key.key_id))
1310 return -EINVAL;
1311
1312 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1313
1314 return ops->llsec->del_devkey(dev, devaddr, &key);
1315}
1316
1317int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info)
1318{
1319 return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey);
1320}
1321
1322static int
1323ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq,
1324 __le64 devaddr,
1325 const struct ieee802154_llsec_device_key *devkey,
1326 const struct net_device *dev)
1327{
1328 void *hdr;
1329
1330 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1331 IEEE802154_LLSEC_LIST_DEVKEY);
1332 if (!hdr)
1333 goto out;
1334
1335 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1336 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1337 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) ||
1338 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1339 devkey->frame_counter) ||
1340 ieee802154_llsec_fill_key_id(msg, &devkey->key_id))
1341 goto nla_put_failure;
1342
1343 genlmsg_end(msg, hdr);
1344 return 0;
1345
1346nla_put_failure:
1347 genlmsg_cancel(msg, hdr);
1348out:
1349 return -EMSGSIZE;
1350}
1351
1352static int llsec_iter_devkeys(struct llsec_dump_data *data)
1353{
1354 struct ieee802154_llsec_device *dpos;
1355 struct ieee802154_llsec_device_key *kpos;
1356 int rc = 0, idx = 0, idx2;
1357
1358 list_for_each_entry(dpos, &data->table->devices, list) {
1359 if (idx++ < data->s_idx)
1360 continue;
1361
1362 idx2 = 0;
1363
1364 list_for_each_entry(kpos, &dpos->keys, list) {
1365 if (idx2++ < data->s_idx2)
1366 continue;
1367
1368 if (ieee802154_nl_fill_devkey(data->skb, data->portid,
1369 data->nlmsg_seq,
1370 dpos->hwaddr, kpos,
1371 data->dev)) {
1372 return rc = -EMSGSIZE;
1373 }
1374
1375 data->s_idx2++;
1376 }
1377
1378 data->s_idx++;
1379 }
1380
1381 return rc;
1382}
1383
1384int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
1385 struct netlink_callback *cb)
1386{
1387 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
1388}
1389
1390
1391
1392static int
1393llsec_parse_seclevel(struct genl_info *info,
1394 struct ieee802154_llsec_seclevel *sl)
1395{
1396 memset(sl, 0, sizeof(*sl));
1397
1398 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] ||
1399 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] ||
1400 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE])
1401 return -EINVAL;
1402
1403 sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]);
1404 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) {
1405 if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID])
1406 return -EINVAL;
1407
1408 sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]);
1409 }
1410
1411 sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]);
1412 sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1413
1414 return 0;
1415}
1416
1417static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info)
1418{
1419 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1420 struct ieee802154_llsec_seclevel sl;
1421
1422 if (llsec_parse_seclevel(info, &sl))
1423 return -EINVAL;
1424
1425 return ops->llsec->add_seclevel(dev, &sl);
1426}
1427
1428int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info)
1429{
1430 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1431 (NLM_F_CREATE | NLM_F_EXCL))
1432 return -EINVAL;
1433
1434 return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel);
1435}
1436
1437static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info)
1438{
1439 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1440 struct ieee802154_llsec_seclevel sl;
1441
1442 if (llsec_parse_seclevel(info, &sl))
1443 return -EINVAL;
1444
1445 return ops->llsec->del_seclevel(dev, &sl);
1446}
1447
1448int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info)
1449{
1450 return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel);
1451}
1452
1453static int
1454ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq,
1455 const struct ieee802154_llsec_seclevel *sl,
1456 const struct net_device *dev)
1457{
1458 void *hdr;
1459
1460 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1461 IEEE802154_LLSEC_LIST_SECLEVEL);
1462 if (!hdr)
1463 goto out;
1464
1465 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1466 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1467 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) ||
1468 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) ||
1469 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1470 sl->device_override))
1471 goto nla_put_failure;
1472
1473 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
1474 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
1475 sl->cmd_frame_id))
1476 goto nla_put_failure;
1477
1478 genlmsg_end(msg, hdr);
1479 return 0;
1480
1481nla_put_failure:
1482 genlmsg_cancel(msg, hdr);
1483out:
1484 return -EMSGSIZE;
1485}
1486
1487static int llsec_iter_seclevels(struct llsec_dump_data *data)
1488{
1489 struct ieee802154_llsec_seclevel *pos;
1490 int rc = 0, idx = 0;
1491
1492 list_for_each_entry(pos, &data->table->security_levels, list) {
1493 if (idx++ < data->s_idx)
1494 continue;
1495
1496 if (ieee802154_nl_fill_seclevel(data->skb, data->portid,
1497 data->nlmsg_seq, pos,
1498 data->dev)) {
1499 rc = -EMSGSIZE;
1500 break;
1501 }
1502
1503 data->s_idx++;
1504 }
1505
1506 return rc;
1507}
1508
1509int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
1510 struct netlink_callback *cb)
1511{
1512 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels);
1513}