blob: cc2919dbe5e01c8b9edae3a069bd804d6447e88d [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;
Alexander Aring8f499f92014-11-02 04:18:39 +0100480 int ret = -EBUSY;
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;
Alexander Aring8f499f92014-11-02 04:18:39 +0100496
497 if (netif_running(dev))
Werner Almesberger56aa0912013-04-04 06:32:35 +0000498 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400499
Alexander Aring8f499f92014-11-02 04:18:39 +0100500 if (!ieee802154_mlme_ops(dev)->start_req) {
501 ret = -EOPNOTSUPP;
502 goto out;
503 }
504
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100505 addr.mode = IEEE802154_ADDR_SHORT;
506 addr.short_addr = nla_get_shortaddr(
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400507 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100508 addr.pan_id = nla_get_shortaddr(
509 info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400510
511 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
512 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
513 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
514 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
515 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
516 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
517
518 if (info->attrs[IEEE802154_ATTR_PAGE])
519 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
520 else
521 page = 0;
522
523
Phoebe Buckheisterae531b92014-03-14 21:24:02 +0100524 if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400525 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
526 dev_put(dev);
527 return -EINVAL;
528 }
529
530 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
531 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
532
Werner Almesberger56aa0912013-04-04 06:32:35 +0000533out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400534 dev_put(dev);
535 return ret;
536}
537
Johannes Berg1c582d92013-11-14 17:14:41 +0100538int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400539{
540 struct net_device *dev;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000541 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400542 u8 type;
543 u32 channels;
544 u8 duration;
545 u8 page;
546
547 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
548 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
549 !info->attrs[IEEE802154_ATTR_DURATION])
550 return -EINVAL;
551
552 dev = ieee802154_nl_get_dev(info);
553 if (!dev)
554 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000555 if (!ieee802154_mlme_ops(dev)->scan_req)
556 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400557
558 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
559 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
560 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
561
562 if (info->attrs[IEEE802154_ATTR_PAGE])
563 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
564 else
565 page = 0;
566
567
Varka Bhadram4710d802014-07-02 09:01:09 +0530568 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
569 page, duration);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400570
Werner Almesberger56aa0912013-04-04 06:32:35 +0000571out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400572 dev_put(dev);
573 return ret;
574}
575
Johannes Berg1c582d92013-11-14 17:14:41 +0100576int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400577{
578 /* Request for interface name, index, type, IEEE address,
Varka Bhadram4710d802014-07-02 09:01:09 +0530579 * PAN Id, short address
580 */
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400581 struct sk_buff *msg;
582 struct net_device *dev = NULL;
583 int rc = -ENOBUFS;
584
585 pr_debug("%s\n", __func__);
586
587 dev = ieee802154_nl_get_dev(info);
588 if (!dev)
589 return -ENODEV;
590
Thomas Graf58050fc2012-06-28 03:57:45 +0000591 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400592 if (!msg)
593 goto out_dev;
594
Eric W. Biederman15e47302012-09-07 20:12:54 +0000595 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Varka Bhadram4710d802014-07-02 09:01:09 +0530596 0, dev);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400597 if (rc < 0)
598 goto out_free;
599
600 dev_put(dev);
601
602 return genlmsg_reply(msg, info);
603out_free:
604 nlmsg_free(msg);
605out_dev:
606 dev_put(dev);
607 return rc;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400608}
609
Johannes Berg1c582d92013-11-14 17:14:41 +0100610int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400611{
612 struct net *net = sock_net(skb->sk);
613 struct net_device *dev;
614 int idx;
615 int s_idx = cb->args[0];
616
617 pr_debug("%s\n", __func__);
618
619 idx = 0;
620 for_each_netdev(net, dev) {
621 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
622 goto cont;
623
Eric W. Biederman15e47302012-09-07 20:12:54 +0000624 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Varka Bhadram4710d802014-07-02 09:01:09 +0530625 cb->nlh->nlmsg_seq,
626 NLM_F_MULTI, dev) < 0)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400627 break;
628cont:
629 idx++;
630 }
631 cb->args[0] = idx;
632
633 return skb->len;
634}
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200635
636int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
637{
638 struct net_device *dev = NULL;
639 struct ieee802154_mlme_ops *ops;
640 struct ieee802154_mac_params params;
641 struct wpan_phy *phy;
642 int rc = -EINVAL;
643
644 pr_debug("%s\n", __func__);
645
646 dev = ieee802154_nl_get_dev(info);
647 if (!dev)
648 return -ENODEV;
649
650 ops = ieee802154_mlme_ops(dev);
651
652 if (!ops->get_mac_params || !ops->set_mac_params) {
653 rc = -EOPNOTSUPP;
654 goto out;
655 }
656
657 if (netif_running(dev)) {
658 rc = -EBUSY;
659 goto out;
660 }
661
662 if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
663 !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
664 !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] &&
665 !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] &&
666 !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] &&
667 !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] &&
668 !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
669 goto out;
670
671 phy = ops->get_phy(dev);
672
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200673 ops->get_mac_params(dev, &params);
674
675 if (info->attrs[IEEE802154_ATTR_TXPOWER])
676 params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
677
678 if (info->attrs[IEEE802154_ATTR_LBT_ENABLED])
679 params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
680
681 if (info->attrs[IEEE802154_ATTR_CCA_MODE])
682 params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
683
684 if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
685 params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
686
687 if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
688 params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
689
690 if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
691 params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
692
693 if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
694 params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
695
696 if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
697 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
698
699 rc = ops->set_mac_params(dev, &params);
700
701 wpan_phy_put(phy);
702 dev_put(dev);
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200703
Alexander Aringa543c592014-10-28 18:21:23 +0100704 return 0;
705
Phoebe Buckheistere462ded2014-03-31 21:37:46 +0200706out:
707 dev_put(dev);
708 return rc;
709}
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200710
711
712
713static int
714ieee802154_llsec_parse_key_id(struct genl_info *info,
715 struct ieee802154_llsec_key_id *desc)
716{
717 memset(desc, 0, sizeof(*desc));
718
719 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE])
720 return -EINVAL;
721
722 desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]);
723
724 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
725 if (!info->attrs[IEEE802154_ATTR_PAN_ID] &&
726 !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] ||
727 info->attrs[IEEE802154_ATTR_HW_ADDR]))
728 return -EINVAL;
729
730 desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
731
732 if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) {
733 desc->device_addr.mode = IEEE802154_ADDR_SHORT;
734 desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
735 } else {
736 desc->device_addr.mode = IEEE802154_ADDR_LONG;
737 desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
738 }
739 }
740
741 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
742 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID])
743 return -EINVAL;
744
745 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
746 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT])
747 return -EINVAL;
748
749 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
750 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED])
751 return -EINVAL;
752
753 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT)
754 desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]);
755
756 switch (desc->mode) {
757 case IEEE802154_SCF_KEY_SHORT_INDEX:
758 {
759 u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]);
Varka Bhadram4710d802014-07-02 09:01:09 +0530760
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200761 desc->short_source = cpu_to_le32(source);
762 break;
763 }
764 case IEEE802154_SCF_KEY_HW_INDEX:
765 desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]);
766 break;
767 }
768
769 return 0;
770}
771
772static int
773ieee802154_llsec_fill_key_id(struct sk_buff *msg,
774 const struct ieee802154_llsec_key_id *desc)
775{
776 if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode))
777 return -EMSGSIZE;
778
779 if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
780 if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID,
781 desc->device_addr.pan_id))
782 return -EMSGSIZE;
783
784 if (desc->device_addr.mode == IEEE802154_ADDR_SHORT &&
785 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
786 desc->device_addr.short_addr))
787 return -EMSGSIZE;
788
789 if (desc->device_addr.mode == IEEE802154_ADDR_LONG &&
790 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR,
791 desc->device_addr.extended_addr))
792 return -EMSGSIZE;
793 }
794
795 if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
796 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id))
797 return -EMSGSIZE;
798
799 if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
800 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
801 le32_to_cpu(desc->short_source)))
802 return -EMSGSIZE;
803
804 if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
805 nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
806 desc->extended_source))
807 return -EMSGSIZE;
808
809 return 0;
810}
811
812int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info)
813{
814 struct sk_buff *msg;
815 struct net_device *dev = NULL;
816 int rc = -ENOBUFS;
817 struct ieee802154_mlme_ops *ops;
818 void *hdr;
819 struct ieee802154_llsec_params params;
820
821 pr_debug("%s\n", __func__);
822
823 dev = ieee802154_nl_get_dev(info);
824 if (!dev)
825 return -ENODEV;
826
827 ops = ieee802154_mlme_ops(dev);
Dan Carpenterb3f7a7b2014-05-22 10:53:06 +0300828 if (!ops->llsec) {
829 rc = -EOPNOTSUPP;
830 goto out_dev;
831 }
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200832
833 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
834 if (!msg)
835 goto out_dev;
836
837 hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0,
Varka Bhadram4710d802014-07-02 09:01:09 +0530838 IEEE802154_LLSEC_GETPARAMS);
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200839 if (!hdr)
840 goto out_free;
841
842 rc = ops->llsec->get_params(dev, &params);
843 if (rc < 0)
844 goto out_free;
845
846 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
847 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
848 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) ||
849 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) ||
850 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
851 be32_to_cpu(params.frame_counter)) ||
852 ieee802154_llsec_fill_key_id(msg, &params.out_key))
853 goto out_free;
854
855 dev_put(dev);
856
857 return ieee802154_nl_reply(msg, info);
858out_free:
859 nlmsg_free(msg);
860out_dev:
861 dev_put(dev);
862 return rc;
863}
864
865int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info)
866{
867 struct net_device *dev = NULL;
868 int rc = -EINVAL;
869 struct ieee802154_mlme_ops *ops;
870 struct ieee802154_llsec_params params;
871 int changed = 0;
872
873 pr_debug("%s\n", __func__);
874
875 dev = ieee802154_nl_get_dev(info);
876 if (!dev)
877 return -ENODEV;
878
879 if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] &&
880 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] &&
881 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL])
882 goto out;
883
884 ops = ieee802154_mlme_ops(dev);
885 if (!ops->llsec) {
886 rc = -EOPNOTSUPP;
887 goto out;
888 }
889
890 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] &&
891 nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7)
892 goto out;
893
894 if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) {
895 params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]);
896 changed |= IEEE802154_LLSEC_PARAM_ENABLED;
897 }
898
899 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) {
900 if (ieee802154_llsec_parse_key_id(info, &params.out_key))
901 goto out;
902
903 changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
904 }
905
906 if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) {
907 params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]);
908 changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
909 }
910
911 if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) {
912 u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
913
914 params.frame_counter = cpu_to_be32(fc);
915 changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
916 }
917
918 rc = ops->llsec->set_params(dev, &params, changed);
919
920 dev_put(dev);
921
922 return rc;
923out:
924 dev_put(dev);
925 return rc;
926}
927
928
929
930struct llsec_dump_data {
931 struct sk_buff *skb;
932 int s_idx, s_idx2;
933 int portid;
934 int nlmsg_seq;
935 struct net_device *dev;
936 struct ieee802154_mlme_ops *ops;
937 struct ieee802154_llsec_table *table;
938};
939
940static int
941ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
Varka Bhadram4710d802014-07-02 09:01:09 +0530942 int (*step)(struct llsec_dump_data *))
Phoebe Buckheister3e9c1562014-05-16 17:46:44 +0200943{
944 struct net *net = sock_net(skb->sk);
945 struct net_device *dev;
946 struct llsec_dump_data data;
947 int idx = 0;
948 int first_dev = cb->args[0];
949 int rc;
950
951 for_each_netdev(net, dev) {
952 if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
953 goto skip;
954
955 data.ops = ieee802154_mlme_ops(dev);
956 if (!data.ops->llsec)
957 goto skip;
958
959 data.skb = skb;
960 data.s_idx = cb->args[1];
961 data.s_idx2 = cb->args[2];
962 data.dev = dev;
963 data.portid = NETLINK_CB(cb->skb).portid;
964 data.nlmsg_seq = cb->nlh->nlmsg_seq;
965
966 data.ops->llsec->lock_table(dev);
967 data.ops->llsec->get_table(data.dev, &data.table);
968 rc = step(&data);
969 data.ops->llsec->unlock_table(dev);
970
971 if (rc < 0)
972 break;
973
974skip:
975 idx++;
976 }
977 cb->args[0] = idx;
978
979 return skb->len;
980}
981
982static int
983ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
984 int (*fn)(struct net_device*, struct genl_info*))
985{
986 struct net_device *dev = NULL;
987 int rc = -EINVAL;
988
989 dev = ieee802154_nl_get_dev(info);
990 if (!dev)
991 return -ENODEV;
992
993 if (!ieee802154_mlme_ops(dev)->llsec)
994 rc = -EOPNOTSUPP;
995 else
996 rc = fn(dev, info);
997
998 dev_put(dev);
999 return rc;
1000}
1001
1002
1003
1004static int
1005ieee802154_llsec_parse_key(struct genl_info *info,
1006 struct ieee802154_llsec_key *key)
1007{
1008 u8 frames;
1009 u32 commands[256 / 32];
1010
1011 memset(key, 0, sizeof(*key));
1012
1013 if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] ||
1014 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES])
1015 return -EINVAL;
1016
1017 frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]);
1018 if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) &&
1019 !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS])
1020 return -EINVAL;
1021
1022 if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) {
1023 nla_memcpy(commands,
1024 info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS],
1025 256 / 8);
1026
1027 if (commands[0] || commands[1] || commands[2] || commands[3] ||
1028 commands[4] || commands[5] || commands[6] ||
1029 commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1))
1030 return -EINVAL;
1031
1032 key->cmd_frame_ids = commands[7];
1033 }
1034
1035 key->frame_types = frames;
1036
1037 nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES],
1038 IEEE802154_LLSEC_KEY_SIZE);
1039
1040 return 0;
1041}
1042
1043static int llsec_add_key(struct net_device *dev, struct genl_info *info)
1044{
1045 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1046 struct ieee802154_llsec_key key;
1047 struct ieee802154_llsec_key_id id;
1048
1049 if (ieee802154_llsec_parse_key(info, &key) ||
1050 ieee802154_llsec_parse_key_id(info, &id))
1051 return -EINVAL;
1052
1053 return ops->llsec->add_key(dev, &id, &key);
1054}
1055
1056int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info)
1057{
1058 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1059 (NLM_F_CREATE | NLM_F_EXCL))
1060 return -EINVAL;
1061
1062 return ieee802154_nl_llsec_change(skb, info, llsec_add_key);
1063}
1064
1065static int llsec_remove_key(struct net_device *dev, struct genl_info *info)
1066{
1067 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1068 struct ieee802154_llsec_key_id id;
1069
1070 if (ieee802154_llsec_parse_key_id(info, &id))
1071 return -EINVAL;
1072
1073 return ops->llsec->del_key(dev, &id);
1074}
1075
1076int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info)
1077{
1078 return ieee802154_nl_llsec_change(skb, info, llsec_remove_key);
1079}
1080
1081static int
1082ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq,
1083 const struct ieee802154_llsec_key_entry *key,
1084 const struct net_device *dev)
1085{
1086 void *hdr;
1087 u32 commands[256 / 32];
1088
1089 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1090 IEEE802154_LLSEC_LIST_KEY);
1091 if (!hdr)
1092 goto out;
1093
1094 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1095 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1096 ieee802154_llsec_fill_key_id(msg, &key->id) ||
1097 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
1098 key->key->frame_types))
1099 goto nla_put_failure;
1100
1101 if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) {
1102 memset(commands, 0, sizeof(commands));
1103 commands[7] = key->key->cmd_frame_ids;
1104 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
1105 sizeof(commands), commands))
1106 goto nla_put_failure;
1107 }
1108
1109 if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES,
1110 IEEE802154_LLSEC_KEY_SIZE, key->key->key))
1111 goto nla_put_failure;
1112
1113 genlmsg_end(msg, hdr);
1114 return 0;
1115
1116nla_put_failure:
1117 genlmsg_cancel(msg, hdr);
1118out:
1119 return -EMSGSIZE;
1120}
1121
1122static int llsec_iter_keys(struct llsec_dump_data *data)
1123{
1124 struct ieee802154_llsec_key_entry *pos;
1125 int rc = 0, idx = 0;
1126
1127 list_for_each_entry(pos, &data->table->keys, list) {
1128 if (idx++ < data->s_idx)
1129 continue;
1130
1131 if (ieee802154_nl_fill_key(data->skb, data->portid,
1132 data->nlmsg_seq, pos, data->dev)) {
1133 rc = -EMSGSIZE;
1134 break;
1135 }
1136
1137 data->s_idx++;
1138 }
1139
1140 return rc;
1141}
1142
1143int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
1144{
1145 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
1146}
1147
1148
1149
1150static int
1151llsec_parse_dev(struct genl_info *info,
1152 struct ieee802154_llsec_device *dev)
1153{
1154 memset(dev, 0, sizeof(*dev));
1155
1156 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1157 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1158 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] ||
1159 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] ||
1160 (!!info->attrs[IEEE802154_ATTR_PAN_ID] !=
1161 !!info->attrs[IEEE802154_ATTR_SHORT_ADDR]))
1162 return -EINVAL;
1163
1164 if (info->attrs[IEEE802154_ATTR_PAN_ID]) {
1165 dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
1166 dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
1167 } else {
1168 dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF);
1169 }
1170
1171 dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1172 dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1173 dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1174 dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]);
1175
1176 if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX)
1177 return -EINVAL;
1178
1179 return 0;
1180}
1181
1182static int llsec_add_dev(struct net_device *dev, struct genl_info *info)
1183{
1184 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1185 struct ieee802154_llsec_device desc;
1186
1187 if (llsec_parse_dev(info, &desc))
1188 return -EINVAL;
1189
1190 return ops->llsec->add_dev(dev, &desc);
1191}
1192
1193int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info)
1194{
1195 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1196 (NLM_F_CREATE | NLM_F_EXCL))
1197 return -EINVAL;
1198
1199 return ieee802154_nl_llsec_change(skb, info, llsec_add_dev);
1200}
1201
1202static int llsec_del_dev(struct net_device *dev, struct genl_info *info)
1203{
1204 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1205 __le64 devaddr;
1206
1207 if (!info->attrs[IEEE802154_ATTR_HW_ADDR])
1208 return -EINVAL;
1209
1210 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1211
1212 return ops->llsec->del_dev(dev, devaddr);
1213}
1214
1215int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info)
1216{
1217 return ieee802154_nl_llsec_change(skb, info, llsec_del_dev);
1218}
1219
1220static int
1221ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq,
1222 const struct ieee802154_llsec_device *desc,
1223 const struct net_device *dev)
1224{
1225 void *hdr;
1226
1227 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1228 IEEE802154_LLSEC_LIST_DEV);
1229 if (!hdr)
1230 goto out;
1231
1232 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1233 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1234 nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) ||
1235 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
1236 desc->short_addr) ||
1237 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) ||
1238 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1239 desc->frame_counter) ||
1240 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1241 desc->seclevel_exempt) ||
1242 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode))
1243 goto nla_put_failure;
1244
1245 genlmsg_end(msg, hdr);
1246 return 0;
1247
1248nla_put_failure:
1249 genlmsg_cancel(msg, hdr);
1250out:
1251 return -EMSGSIZE;
1252}
1253
1254static int llsec_iter_devs(struct llsec_dump_data *data)
1255{
1256 struct ieee802154_llsec_device *pos;
1257 int rc = 0, idx = 0;
1258
1259 list_for_each_entry(pos, &data->table->devices, list) {
1260 if (idx++ < data->s_idx)
1261 continue;
1262
1263 if (ieee802154_nl_fill_dev(data->skb, data->portid,
1264 data->nlmsg_seq, pos, data->dev)) {
1265 rc = -EMSGSIZE;
1266 break;
1267 }
1268
1269 data->s_idx++;
1270 }
1271
1272 return rc;
1273}
1274
1275int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
1276{
1277 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
1278}
1279
1280
1281
1282static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
1283{
1284 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1285 struct ieee802154_llsec_device_key key;
1286 __le64 devaddr;
1287
1288 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
1289 !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1290 ieee802154_llsec_parse_key_id(info, &key.key_id))
1291 return -EINVAL;
1292
1293 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1294 key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
1295
1296 return ops->llsec->add_devkey(dev, devaddr, &key);
1297}
1298
1299int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info)
1300{
1301 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1302 (NLM_F_CREATE | NLM_F_EXCL))
1303 return -EINVAL;
1304
1305 return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey);
1306}
1307
1308static int llsec_del_devkey(struct net_device *dev, struct genl_info *info)
1309{
1310 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1311 struct ieee802154_llsec_device_key key;
1312 __le64 devaddr;
1313
1314 if (!info->attrs[IEEE802154_ATTR_HW_ADDR] ||
1315 ieee802154_llsec_parse_key_id(info, &key.key_id))
1316 return -EINVAL;
1317
1318 devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
1319
1320 return ops->llsec->del_devkey(dev, devaddr, &key);
1321}
1322
1323int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info)
1324{
1325 return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey);
1326}
1327
1328static int
1329ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq,
1330 __le64 devaddr,
1331 const struct ieee802154_llsec_device_key *devkey,
1332 const struct net_device *dev)
1333{
1334 void *hdr;
1335
1336 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1337 IEEE802154_LLSEC_LIST_DEVKEY);
1338 if (!hdr)
1339 goto out;
1340
1341 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1342 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1343 nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) ||
1344 nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
1345 devkey->frame_counter) ||
1346 ieee802154_llsec_fill_key_id(msg, &devkey->key_id))
1347 goto nla_put_failure;
1348
1349 genlmsg_end(msg, hdr);
1350 return 0;
1351
1352nla_put_failure:
1353 genlmsg_cancel(msg, hdr);
1354out:
1355 return -EMSGSIZE;
1356}
1357
1358static int llsec_iter_devkeys(struct llsec_dump_data *data)
1359{
1360 struct ieee802154_llsec_device *dpos;
1361 struct ieee802154_llsec_device_key *kpos;
1362 int rc = 0, idx = 0, idx2;
1363
1364 list_for_each_entry(dpos, &data->table->devices, list) {
1365 if (idx++ < data->s_idx)
1366 continue;
1367
1368 idx2 = 0;
1369
1370 list_for_each_entry(kpos, &dpos->keys, list) {
1371 if (idx2++ < data->s_idx2)
1372 continue;
1373
1374 if (ieee802154_nl_fill_devkey(data->skb, data->portid,
1375 data->nlmsg_seq,
1376 dpos->hwaddr, kpos,
1377 data->dev)) {
1378 return rc = -EMSGSIZE;
1379 }
1380
1381 data->s_idx2++;
1382 }
1383
1384 data->s_idx++;
1385 }
1386
1387 return rc;
1388}
1389
1390int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
1391 struct netlink_callback *cb)
1392{
1393 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
1394}
1395
1396
1397
1398static int
1399llsec_parse_seclevel(struct genl_info *info,
1400 struct ieee802154_llsec_seclevel *sl)
1401{
1402 memset(sl, 0, sizeof(*sl));
1403
1404 if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] ||
1405 !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] ||
1406 !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE])
1407 return -EINVAL;
1408
1409 sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]);
1410 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) {
1411 if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID])
1412 return -EINVAL;
1413
1414 sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]);
1415 }
1416
1417 sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]);
1418 sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
1419
1420 return 0;
1421}
1422
1423static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info)
1424{
1425 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1426 struct ieee802154_llsec_seclevel sl;
1427
1428 if (llsec_parse_seclevel(info, &sl))
1429 return -EINVAL;
1430
1431 return ops->llsec->add_seclevel(dev, &sl);
1432}
1433
1434int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info)
1435{
1436 if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
1437 (NLM_F_CREATE | NLM_F_EXCL))
1438 return -EINVAL;
1439
1440 return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel);
1441}
1442
1443static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info)
1444{
1445 struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
1446 struct ieee802154_llsec_seclevel sl;
1447
1448 if (llsec_parse_seclevel(info, &sl))
1449 return -EINVAL;
1450
1451 return ops->llsec->del_seclevel(dev, &sl);
1452}
1453
1454int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info)
1455{
1456 return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel);
1457}
1458
1459static int
1460ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq,
1461 const struct ieee802154_llsec_seclevel *sl,
1462 const struct net_device *dev)
1463{
1464 void *hdr;
1465
1466 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
1467 IEEE802154_LLSEC_LIST_SECLEVEL);
1468 if (!hdr)
1469 goto out;
1470
1471 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
1472 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
1473 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) ||
1474 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) ||
1475 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
1476 sl->device_override))
1477 goto nla_put_failure;
1478
1479 if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
1480 nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
1481 sl->cmd_frame_id))
1482 goto nla_put_failure;
1483
1484 genlmsg_end(msg, hdr);
1485 return 0;
1486
1487nla_put_failure:
1488 genlmsg_cancel(msg, hdr);
1489out:
1490 return -EMSGSIZE;
1491}
1492
1493static int llsec_iter_seclevels(struct llsec_dump_data *data)
1494{
1495 struct ieee802154_llsec_seclevel *pos;
1496 int rc = 0, idx = 0;
1497
1498 list_for_each_entry(pos, &data->table->security_levels, list) {
1499 if (idx++ < data->s_idx)
1500 continue;
1501
1502 if (ieee802154_nl_fill_seclevel(data->skb, data->portid,
1503 data->nlmsg_seq, pos,
1504 data->dev)) {
1505 rc = -EMSGSIZE;
1506 break;
1507 }
1508
1509 data->s_idx++;
1510 }
1511
1512 return rc;
1513}
1514
1515int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
1516 struct netlink_callback *cb)
1517{
1518 return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels);
1519}