Root/net/ieee802154/nl-phy.c

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

Archive Download this file



interactive