linux/net/wireless/nl80211.c
<<
>>
Prefs
   1/*
   2 * This is the new netlink-based wireless configuration interface.
   3 *
   4 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
   5 */
   6
   7#include <linux/if.h>
   8#include <linux/module.h>
   9#include <linux/err.h>
  10#include <linux/mutex.h>
  11#include <linux/list.h>
  12#include <linux/if_ether.h>
  13#include <linux/ieee80211.h>
  14#include <linux/nl80211.h>
  15#include <linux/rtnetlink.h>
  16#include <linux/netlink.h>
  17#include <net/genetlink.h>
  18#include <net/cfg80211.h>
  19#include "core.h"
  20#include "nl80211.h"
  21
  22/* the netlink family */
  23static struct genl_family nl80211_fam = {
  24        .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
  25        .name = "nl80211",      /* have users key off the name instead */
  26        .hdrsize = 0,           /* no private header */
  27        .version = 1,           /* no particular meaning now */
  28        .maxattr = NL80211_ATTR_MAX,
  29};
  30
  31/* internal helper: get drv and dev */
  32static int get_drv_dev_by_info_ifindex(struct genl_info *info,
  33                                       struct cfg80211_registered_device **drv,
  34                                       struct net_device **dev)
  35{
  36        int ifindex;
  37
  38        if (!info->attrs[NL80211_ATTR_IFINDEX])
  39                return -EINVAL;
  40
  41        ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
  42        *dev = dev_get_by_index(&init_net, ifindex);
  43        if (!*dev)
  44                return -ENODEV;
  45
  46        *drv = cfg80211_get_dev_from_ifindex(ifindex);
  47        if (IS_ERR(*drv)) {
  48                dev_put(*dev);
  49                return PTR_ERR(*drv);
  50        }
  51
  52        return 0;
  53}
  54
  55/* policy for the attributes */
  56static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
  57        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
  58        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
  59                                      .len = BUS_ID_SIZE-1 },
  60
  61        [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
  62        [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
  63        [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
  64};
  65
  66/* message building helper */
  67static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
  68                                   int flags, u8 cmd)
  69{
  70        /* since there is no private header just add the generic one */
  71        return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
  72}
  73
  74/* netlink command implementations */
  75
  76static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
  77                              struct cfg80211_registered_device *dev)
  78{
  79        void *hdr;
  80
  81        hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
  82        if (!hdr)
  83                return -1;
  84
  85        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
  86        NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
  87        return genlmsg_end(msg, hdr);
  88
  89 nla_put_failure:
  90        return genlmsg_cancel(msg, hdr);
  91}
  92
  93static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
  94{
  95        int idx = 0;
  96        int start = cb->args[0];
  97        struct cfg80211_registered_device *dev;
  98
  99        mutex_lock(&cfg80211_drv_mutex);
 100        list_for_each_entry(dev, &cfg80211_drv_list, list) {
 101                if (++idx < start)
 102                        continue;
 103                if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
 104                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
 105                                       dev) < 0)
 106                        break;
 107        }
 108        mutex_unlock(&cfg80211_drv_mutex);
 109
 110        cb->args[0] = idx;
 111
 112        return skb->len;
 113}
 114
 115static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
 116{
 117        struct sk_buff *msg;
 118        struct cfg80211_registered_device *dev;
 119
 120        dev = cfg80211_get_dev_from_info(info);
 121        if (IS_ERR(dev))
 122                return PTR_ERR(dev);
 123
 124        msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 125        if (!msg)
 126                goto out_err;
 127
 128        if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
 129                goto out_free;
 130
 131        cfg80211_put_dev(dev);
 132
 133        return genlmsg_unicast(msg, info->snd_pid);
 134
 135 out_free:
 136        nlmsg_free(msg);
 137 out_err:
 138        cfg80211_put_dev(dev);
 139        return -ENOBUFS;
 140}
 141
 142static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 143{
 144        struct cfg80211_registered_device *rdev;
 145        int result;
 146
 147        if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
 148                return -EINVAL;
 149
 150        rdev = cfg80211_get_dev_from_info(info);
 151        if (IS_ERR(rdev))
 152                return PTR_ERR(rdev);
 153
 154        result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 155
 156        cfg80211_put_dev(rdev);
 157        return result;
 158}
 159
 160
 161static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 162                              struct net_device *dev)
 163{
 164        void *hdr;
 165
 166        hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
 167        if (!hdr)
 168                return -1;
 169
 170        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
 171        NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
 172        /* TODO: interface type */
 173        return genlmsg_end(msg, hdr);
 174
 175 nla_put_failure:
 176        return genlmsg_cancel(msg, hdr);
 177}
 178
 179static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
 180{
 181        int wp_idx = 0;
 182        int if_idx = 0;
 183        int wp_start = cb->args[0];
 184        int if_start = cb->args[1];
 185        struct cfg80211_registered_device *dev;
 186        struct wireless_dev *wdev;
 187
 188        mutex_lock(&cfg80211_drv_mutex);
 189        list_for_each_entry(dev, &cfg80211_drv_list, list) {
 190                if (++wp_idx < wp_start)
 191                        continue;
 192                if_idx = 0;
 193
 194                mutex_lock(&dev->devlist_mtx);
 195                list_for_each_entry(wdev, &dev->netdev_list, list) {
 196                        if (++if_idx < if_start)
 197                                continue;
 198                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
 199                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
 200                                               wdev->netdev) < 0)
 201                                break;
 202                }
 203                mutex_unlock(&dev->devlist_mtx);
 204        }
 205        mutex_unlock(&cfg80211_drv_mutex);
 206
 207        cb->args[0] = wp_idx;
 208        cb->args[1] = if_idx;
 209
 210        return skb->len;
 211}
 212
 213static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
 214{
 215        struct sk_buff *msg;
 216        struct cfg80211_registered_device *dev;
 217        struct net_device *netdev;
 218        int err;
 219
 220        err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
 221        if (err)
 222                return err;
 223
 224        msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 225        if (!msg)
 226                goto out_err;
 227
 228        if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
 229                goto out_free;
 230
 231        dev_put(netdev);
 232        cfg80211_put_dev(dev);
 233
 234        return genlmsg_unicast(msg, info->snd_pid);
 235
 236 out_free:
 237        nlmsg_free(msg);
 238 out_err:
 239        dev_put(netdev);
 240        cfg80211_put_dev(dev);
 241        return -ENOBUFS;
 242}
 243
 244static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 245{
 246        struct cfg80211_registered_device *drv;
 247        int err, ifindex;
 248        enum nl80211_iftype type;
 249        struct net_device *dev;
 250
 251        if (info->attrs[NL80211_ATTR_IFTYPE]) {
 252                type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
 253                if (type > NL80211_IFTYPE_MAX)
 254                        return -EINVAL;
 255        } else
 256                return -EINVAL;
 257
 258        err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
 259        if (err)
 260                return err;
 261        ifindex = dev->ifindex;
 262        dev_put(dev);
 263
 264        if (!drv->ops->change_virtual_intf) {
 265                err = -EOPNOTSUPP;
 266                goto unlock;
 267        }
 268
 269        rtnl_lock();
 270        err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);
 271        rtnl_unlock();
 272
 273 unlock:
 274        cfg80211_put_dev(drv);
 275        return err;
 276}
 277
 278static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 279{
 280        struct cfg80211_registered_device *drv;
 281        int err;
 282        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
 283
 284        if (!info->attrs[NL80211_ATTR_IFNAME])
 285                return -EINVAL;
 286
 287        if (info->attrs[NL80211_ATTR_IFTYPE]) {
 288                type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
 289                if (type > NL80211_IFTYPE_MAX)
 290                        return -EINVAL;
 291        }
 292
 293        drv = cfg80211_get_dev_from_info(info);
 294        if (IS_ERR(drv))
 295                return PTR_ERR(drv);
 296
 297        if (!drv->ops->add_virtual_intf) {
 298                err = -EOPNOTSUPP;
 299                goto unlock;
 300        }
 301
 302        rtnl_lock();
 303        err = drv->ops->add_virtual_intf(&drv->wiphy,
 304                nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
 305        rtnl_unlock();
 306
 307 unlock:
 308        cfg80211_put_dev(drv);
 309        return err;
 310}
 311
 312static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 313{
 314        struct cfg80211_registered_device *drv;
 315        int ifindex, err;
 316        struct net_device *dev;
 317
 318        err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
 319        if (err)
 320                return err;
 321        ifindex = dev->ifindex;
 322        dev_put(dev);
 323
 324        if (!drv->ops->del_virtual_intf) {
 325                err = -EOPNOTSUPP;
 326                goto out;
 327        }
 328
 329        rtnl_lock();
 330        err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
 331        rtnl_unlock();
 332
 333 out:
 334        cfg80211_put_dev(drv);
 335        return err;
 336}
 337
 338static struct genl_ops nl80211_ops[] = {
 339        {
 340                .cmd = NL80211_CMD_GET_WIPHY,
 341                .doit = nl80211_get_wiphy,
 342                .dumpit = nl80211_dump_wiphy,
 343                .policy = nl80211_policy,
 344                /* can be retrieved by unprivileged users */
 345        },
 346        {
 347                .cmd = NL80211_CMD_SET_WIPHY,
 348                .doit = nl80211_set_wiphy,
 349                .policy = nl80211_policy,
 350                .flags = GENL_ADMIN_PERM,
 351        },
 352        {
 353                .cmd = NL80211_CMD_GET_INTERFACE,
 354                .doit = nl80211_get_interface,
 355                .dumpit = nl80211_dump_interface,
 356                .policy = nl80211_policy,
 357                /* can be retrieved by unprivileged users */
 358        },
 359        {
 360                .cmd = NL80211_CMD_SET_INTERFACE,
 361                .doit = nl80211_set_interface,
 362                .policy = nl80211_policy,
 363                .flags = GENL_ADMIN_PERM,
 364        },
 365        {
 366                .cmd = NL80211_CMD_NEW_INTERFACE,
 367                .doit = nl80211_new_interface,
 368                .policy = nl80211_policy,
 369                .flags = GENL_ADMIN_PERM,
 370        },
 371        {
 372                .cmd = NL80211_CMD_DEL_INTERFACE,
 373                .doit = nl80211_del_interface,
 374                .policy = nl80211_policy,
 375                .flags = GENL_ADMIN_PERM,
 376        },
 377};
 378
 379/* multicast groups */
 380static struct genl_multicast_group nl80211_config_mcgrp = {
 381        .name = "config",
 382};
 383
 384/* notification functions */
 385
 386void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
 387{
 388        struct sk_buff *msg;
 389
 390        msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 391        if (!msg)
 392                return;
 393
 394        if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
 395                nlmsg_free(msg);
 396                return;
 397        }
 398
 399        genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
 400}
 401
 402/* initialisation/exit functions */
 403
 404int nl80211_init(void)
 405{
 406        int err, i;
 407
 408        err = genl_register_family(&nl80211_fam);
 409        if (err)
 410                return err;
 411
 412        for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
 413                err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
 414                if (err)
 415                        goto err_out;
 416        }
 417
 418        err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
 419        if (err)
 420                goto err_out;
 421
 422        return 0;
 423 err_out:
 424        genl_unregister_family(&nl80211_fam);
 425        return err;
 426}
 427
 428void nl80211_exit(void)
 429{
 430        genl_unregister_family(&nl80211_fam);
 431}
 432