Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.24-rc7 431 lines 9.7 kB view raw
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}