Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ieee802154/nl-phy.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Netlink interface for IEEE 802.15.4 stack
4
*
5
* Copyright 2007, 2008 Siemens AG
6
*
7
* Written by:
8
* Sergey Lapin <[email protected]>
9
* Dmitry Eremin-Solenikov <[email protected]>
10
* Maxim Osipov <[email protected]>
11
*/
12
13
#include <linux/kernel.h>
14
#include <linux/slab.h>
15
#include <linux/if_arp.h>
16
#include <net/netlink.h>
17
#include <net/genetlink.h>
18
#include <net/cfg802154.h>
19
#include <net/af_ieee802154.h>
20
#include <net/ieee802154_netdev.h>
21
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
22
#include <linux/nl802154.h>
23
24
#include "ieee802154.h"
25
#include "rdev-ops.h"
26
#include "core.h"
27
28
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
29
u32 seq, int flags, struct wpan_phy *phy)
30
{
31
void *hdr;
32
int i, pages = 0;
33
u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), GFP_KERNEL);
34
35
pr_debug("%s\n", __func__);
36
37
if (!buf)
38
return -EMSGSIZE;
39
40
hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
41
IEEE802154_LIST_PHY);
42
if (!hdr)
43
goto out;
44
45
rtnl_lock();
46
if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
47
nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
48
nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel))
49
goto nla_put_failure;
50
for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
51
if (phy->supported.channels[i])
52
buf[pages++] = phy->supported.channels[i] | (i << 27);
53
}
54
if (pages &&
55
nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
56
pages * sizeof(uint32_t), buf))
57
goto nla_put_failure;
58
rtnl_unlock();
59
kfree(buf);
60
genlmsg_end(msg, hdr);
61
return 0;
62
63
nla_put_failure:
64
rtnl_unlock();
65
genlmsg_cancel(msg, hdr);
66
out:
67
kfree(buf);
68
return -EMSGSIZE;
69
}
70
71
int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
72
{
73
/* Request for interface name, index, type, IEEE address,
74
* PAN Id, short address
75
*/
76
struct sk_buff *msg;
77
struct wpan_phy *phy;
78
const char *name;
79
int rc = -ENOBUFS;
80
81
pr_debug("%s\n", __func__);
82
83
if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
84
return -EINVAL;
85
86
name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
87
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
88
return -EINVAL; /* phy name should be null-terminated */
89
90
phy = wpan_phy_find(name);
91
if (!phy)
92
return -ENODEV;
93
94
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
95
if (!msg)
96
goto out_dev;
97
98
rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
99
0, phy);
100
if (rc < 0)
101
goto out_free;
102
103
wpan_phy_put(phy);
104
105
return genlmsg_reply(msg, info);
106
out_free:
107
nlmsg_free(msg);
108
out_dev:
109
wpan_phy_put(phy);
110
return rc;
111
}
112
113
struct dump_phy_data {
114
struct sk_buff *skb;
115
struct netlink_callback *cb;
116
int idx, s_idx;
117
};
118
119
static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
120
{
121
int rc;
122
struct dump_phy_data *data = _data;
123
124
pr_debug("%s\n", __func__);
125
126
if (data->idx++ < data->s_idx)
127
return 0;
128
129
rc = ieee802154_nl_fill_phy(data->skb,
130
NETLINK_CB(data->cb->skb).portid,
131
data->cb->nlh->nlmsg_seq,
132
NLM_F_MULTI,
133
phy);
134
135
if (rc < 0) {
136
data->idx--;
137
return rc;
138
}
139
140
return 0;
141
}
142
143
int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
144
{
145
struct dump_phy_data data = {
146
.cb = cb,
147
.skb = skb,
148
.s_idx = cb->args[0],
149
.idx = 0,
150
};
151
152
pr_debug("%s\n", __func__);
153
154
wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
155
156
cb->args[0] = data.idx;
157
158
return skb->len;
159
}
160
161
int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
162
{
163
struct sk_buff *msg;
164
struct wpan_phy *phy;
165
const char *name;
166
const char *devname;
167
int rc = -ENOBUFS;
168
struct net_device *dev;
169
int type = __IEEE802154_DEV_INVALID;
170
unsigned char name_assign_type;
171
172
pr_debug("%s\n", __func__);
173
174
if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
175
return -EINVAL;
176
177
name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
178
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
179
return -EINVAL; /* phy name should be null-terminated */
180
181
if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
182
devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
183
if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
184
!= '\0')
185
return -EINVAL; /* phy name should be null-terminated */
186
name_assign_type = NET_NAME_USER;
187
} else {
188
devname = "wpan%d";
189
name_assign_type = NET_NAME_ENUM;
190
}
191
192
if (strlen(devname) >= IFNAMSIZ)
193
return -ENAMETOOLONG;
194
195
phy = wpan_phy_find(name);
196
if (!phy)
197
return -ENODEV;
198
199
msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
200
if (!msg)
201
goto out_dev;
202
203
if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
204
nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
205
IEEE802154_ADDR_LEN) {
206
rc = -EINVAL;
207
goto nla_put_failure;
208
}
209
210
if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
211
type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
212
if (type >= __IEEE802154_DEV_MAX) {
213
rc = -EINVAL;
214
goto nla_put_failure;
215
}
216
}
217
218
dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
219
name_assign_type, type);
220
if (IS_ERR(dev)) {
221
rc = PTR_ERR(dev);
222
goto nla_put_failure;
223
}
224
dev_hold(dev);
225
226
if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
227
struct sockaddr_storage addr;
228
229
addr.ss_family = ARPHRD_IEEE802154;
230
nla_memcpy(&addr.__data, info->attrs[IEEE802154_ATTR_HW_ADDR],
231
IEEE802154_ADDR_LEN);
232
233
/* strangely enough, some callbacks (inetdev_event) from
234
* dev_set_mac_address require RTNL_LOCK
235
*/
236
rtnl_lock();
237
rc = dev_set_mac_address(dev, &addr, NULL);
238
rtnl_unlock();
239
if (rc)
240
goto dev_unregister;
241
}
242
243
if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
244
nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) {
245
rc = -EMSGSIZE;
246
goto nla_put_failure;
247
}
248
dev_put(dev);
249
250
wpan_phy_put(phy);
251
252
return ieee802154_nl_reply(msg, info);
253
254
dev_unregister:
255
rtnl_lock(); /* del_iface must be called with RTNL lock */
256
rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
257
dev_put(dev);
258
rtnl_unlock();
259
nla_put_failure:
260
nlmsg_free(msg);
261
out_dev:
262
wpan_phy_put(phy);
263
return rc;
264
}
265
266
int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
267
{
268
struct sk_buff *msg;
269
struct wpan_phy *phy;
270
const char *name;
271
int rc;
272
struct net_device *dev;
273
274
pr_debug("%s\n", __func__);
275
276
if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
277
return -EINVAL;
278
279
name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
280
if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
281
return -EINVAL; /* name should be null-terminated */
282
283
rc = -ENODEV;
284
dev = dev_get_by_name(genl_info_net(info), name);
285
if (!dev)
286
return rc;
287
if (dev->type != ARPHRD_IEEE802154)
288
goto out;
289
290
phy = dev->ieee802154_ptr->wpan_phy;
291
BUG_ON(!phy);
292
get_device(&phy->dev);
293
294
rc = -EINVAL;
295
/* phy name is optional, but should be checked if it's given */
296
if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
297
struct wpan_phy *phy2;
298
299
const char *pname =
300
nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
301
if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
302
!= '\0')
303
/* name should be null-terminated */
304
goto out_dev;
305
306
phy2 = wpan_phy_find(pname);
307
if (!phy2)
308
goto out_dev;
309
310
if (phy != phy2) {
311
wpan_phy_put(phy2);
312
goto out_dev;
313
}
314
}
315
316
rc = -ENOBUFS;
317
318
msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
319
if (!msg)
320
goto out_dev;
321
322
rtnl_lock();
323
rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
324
325
/* We don't have device anymore */
326
dev_put(dev);
327
dev = NULL;
328
329
rtnl_unlock();
330
331
if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
332
nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
333
goto nla_put_failure;
334
wpan_phy_put(phy);
335
336
return ieee802154_nl_reply(msg, info);
337
338
nla_put_failure:
339
nlmsg_free(msg);
340
out_dev:
341
wpan_phy_put(phy);
342
out:
343
dev_put(dev);
344
345
return rc;
346
}
347
348