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