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

[NL80211]: add netlink interface to cfg80211

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Johannes Berg and committed by
David S. Miller
55682965 0800f170

+762 -9
+94 -3
include/linux/nl80211.h
··· 7 7 */ 8 8 9 9 /** 10 + * enum nl80211_commands - supported nl80211 commands 11 + * 12 + * @NL80211_CMD_UNSPEC: unspecified command to catch errors 13 + * 14 + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request 15 + * to get a list of all present wiphys. 16 + * @NL80211_CMD_SET_WIPHY: set wiphy name, needs %NL80211_ATTR_WIPHY and 17 + * %NL80211_ATTR_WIPHY_NAME. 18 + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request 19 + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and 20 + * %NL80211_ATTR_WIPHY_NAME. 21 + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes 22 + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. 23 + * 24 + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; 25 + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get 26 + * on an %NL80211_ATTR_IFINDEX is supported. 27 + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires 28 + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. 29 + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response 30 + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, 31 + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also 32 + * be sent from userspace to request creation of a new virtual interface, 33 + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and 34 + * %NL80211_ATTR_IFNAME. 35 + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes 36 + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from 37 + * userspace to request deletion of a virtual interface, then requires 38 + * attribute %NL80211_ATTR_IFINDEX. 39 + * 40 + * @NL80211_CMD_MAX: highest used command number 41 + * @__NL80211_CMD_AFTER_LAST: internal use 42 + */ 43 + enum nl80211_commands { 44 + /* don't change the order or add anything inbetween, this is ABI! */ 45 + NL80211_CMD_UNSPEC, 46 + 47 + NL80211_CMD_GET_WIPHY, /* can dump */ 48 + NL80211_CMD_SET_WIPHY, 49 + NL80211_CMD_NEW_WIPHY, 50 + NL80211_CMD_DEL_WIPHY, 51 + 52 + NL80211_CMD_GET_INTERFACE, /* can dump */ 53 + NL80211_CMD_SET_INTERFACE, 54 + NL80211_CMD_NEW_INTERFACE, 55 + NL80211_CMD_DEL_INTERFACE, 56 + 57 + /* add commands here */ 58 + 59 + /* used to define NL80211_CMD_MAX below */ 60 + __NL80211_CMD_AFTER_LAST, 61 + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 62 + }; 63 + 64 + 65 + /** 66 + * enum nl80211_attrs - nl80211 netlink attributes 67 + * 68 + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors 69 + * 70 + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. 71 + * /sys/class/ieee80211/<phyname>/index 72 + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) 73 + * 74 + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on 75 + * @NL80211_ATTR_IFNAME: network interface name 76 + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype 77 + * 78 + * @NL80211_ATTR_MAX: highest attribute number currently defined 79 + * @__NL80211_ATTR_AFTER_LAST: internal use 80 + */ 81 + enum nl80211_attrs { 82 + /* don't change the order or add anything inbetween, this is ABI! */ 83 + NL80211_ATTR_UNSPEC, 84 + 85 + NL80211_ATTR_WIPHY, 86 + NL80211_ATTR_WIPHY_NAME, 87 + 88 + NL80211_ATTR_IFINDEX, 89 + NL80211_ATTR_IFNAME, 90 + NL80211_ATTR_IFTYPE, 91 + 92 + /* add attributes here, update the policy in nl80211.c */ 93 + 94 + __NL80211_ATTR_AFTER_LAST, 95 + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 96 + }; 97 + 98 + /** 10 99 * enum nl80211_iftype - (virtual) interface types 100 + * 11 101 * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides 12 102 * @NL80211_IFTYPE_ADHOC: independent BSS member 13 103 * @NL80211_IFTYPE_STATION: managed BSS member ··· 105 15 * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points 106 16 * @NL80211_IFTYPE_WDS: wireless distribution interface 107 17 * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames 18 + * @NL80211_IFTYPE_MAX: highest interface type number currently defined 108 19 * @__NL80211_IFTYPE_AFTER_LAST: internal use 109 20 * 110 - * These values are used with the NL80211_ATTR_IFTYPE 21 + * These values are used with the %NL80211_ATTR_IFTYPE 111 22 * to set the type of an interface. 112 23 * 113 24 */ ··· 122 31 NL80211_IFTYPE_MONITOR, 123 32 124 33 /* keep last */ 125 - __NL80211_IFTYPE_AFTER_LAST 34 + __NL80211_IFTYPE_AFTER_LAST, 35 + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 126 36 }; 127 - #define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1) 128 37 129 38 #endif /* __LINUX_NL80211_H */
+8 -3
include/net/cfg80211.h
··· 3 3 4 4 #include <linux/netlink.h> 5 5 #include <linux/skbuff.h> 6 + #include <linux/nl80211.h> 6 7 #include <net/genetlink.h> 7 8 8 9 /* 9 10 * 802.11 configuration in-kernel interface 10 11 * 11 - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 12 + * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net> 12 13 */ 13 - 14 14 15 15 /* Radiotap header iteration 16 16 * implemented in net/wireless/radiotap.c ··· 68 68 * @add_virtual_intf: create a new virtual interface with the given name 69 69 * 70 70 * @del_virtual_intf: remove the virtual interface determined by ifindex. 71 + * 72 + * @change_virtual_intf: change type of virtual interface 73 + * 71 74 */ 72 75 struct cfg80211_ops { 73 76 int (*add_virtual_intf)(struct wiphy *wiphy, char *name, 74 - unsigned int type); 77 + enum nl80211_iftype type); 75 78 int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); 79 + int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex, 80 + enum nl80211_iftype type); 76 81 }; 77 82 78 83 #endif /* __NET_CFG80211_H */
+7 -1
include/net/iw_handler.h
··· 431 431 * Those may be called only within the kernel. 432 432 */ 433 433 434 - /* functions that may be called by driver modules */ 434 + /* First : function strictly used inside the kernel */ 435 + 436 + /* Handle /proc/net/wireless, called in net/code/dev.c */ 437 + extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, 438 + int length); 439 + 440 + /* Second : functions that may be called by driver modules */ 435 441 436 442 /* Send a single event to user space */ 437 443 extern void wireless_send_event(struct net_device * dev,
+1 -1
net/mac80211/ieee80211_cfg.c
··· 14 14 #include "ieee80211_cfg.h" 15 15 16 16 static int ieee80211_add_iface(struct wiphy *wiphy, char *name, 17 - unsigned int type) 17 + enum nl80211_iftype type) 18 18 { 19 19 struct ieee80211_local *local = wiphy_priv(wiphy); 20 20 int itype;
+16 -1
net/wireless/Kconfig
··· 1 1 config CFG80211 2 2 tristate "Improved wireless configuration API" 3 3 4 + config NL80211 5 + bool "nl80211 new netlink interface support" 6 + depends CFG80211 7 + default y 8 + ---help--- 9 + This option turns on the new netlink interface 10 + (nl80211) support in cfg80211. 11 + 12 + If =n, drivers using mac80211 will be configured via 13 + wireless extension support provided by that subsystem. 14 + 15 + If unsure, say Y. 16 + 4 17 config WIRELESS_EXT 5 18 bool "Wireless extensions" 6 19 default n ··· 23 10 24 11 Wireless extensions will be replaced by cfg80211 and 25 12 will be required only by legacy drivers that implement 26 - wireless extension handlers. 13 + wireless extension handlers. This option does not 14 + affect the wireless-extension backward compatibility 15 + code in cfg80211. 27 16 28 17 Say N (if you can) unless you know you need wireless 29 18 extensions for external modules.
+1
net/wireless/Makefile
··· 2 2 obj-$(CONFIG_CFG80211) += cfg80211.o 3 3 4 4 cfg80211-y += core.o sysfs.o radiotap.o 5 + cfg80211-$(CONFIG_NL80211) += nl80211.o
+148
net/wireless/core.c
··· 16 16 #include <net/genetlink.h> 17 17 #include <net/cfg80211.h> 18 18 #include <net/wireless.h> 19 + #include "nl80211.h" 19 20 #include "core.h" 20 21 #include "sysfs.h" 21 22 ··· 36 35 37 36 /* for debugfs */ 38 37 static struct dentry *ieee80211_debugfs_dir; 38 + 39 + /* requires cfg80211_drv_mutex to be held! */ 40 + static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy) 41 + { 42 + struct cfg80211_registered_device *result = NULL, *drv; 43 + 44 + list_for_each_entry(drv, &cfg80211_drv_list, list) { 45 + if (drv->idx == wiphy) { 46 + result = drv; 47 + break; 48 + } 49 + } 50 + 51 + return result; 52 + } 53 + 54 + /* requires cfg80211_drv_mutex to be held! */ 55 + static struct cfg80211_registered_device * 56 + __cfg80211_drv_from_info(struct genl_info *info) 57 + { 58 + int ifindex; 59 + struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL; 60 + struct net_device *dev; 61 + int err = -EINVAL; 62 + 63 + if (info->attrs[NL80211_ATTR_WIPHY]) { 64 + bywiphy = cfg80211_drv_by_wiphy( 65 + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); 66 + err = -ENODEV; 67 + } 68 + 69 + if (info->attrs[NL80211_ATTR_IFINDEX]) { 70 + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); 71 + dev = dev_get_by_index(&init_net, ifindex); 72 + if (dev) { 73 + if (dev->ieee80211_ptr) 74 + byifidx = 75 + wiphy_to_dev(dev->ieee80211_ptr->wiphy); 76 + dev_put(dev); 77 + } 78 + err = -ENODEV; 79 + } 80 + 81 + if (bywiphy && byifidx) { 82 + if (bywiphy != byifidx) 83 + return ERR_PTR(-EINVAL); 84 + else 85 + return bywiphy; /* == byifidx */ 86 + } 87 + if (bywiphy) 88 + return bywiphy; 89 + 90 + if (byifidx) 91 + return byifidx; 92 + 93 + return ERR_PTR(err); 94 + } 95 + 96 + struct cfg80211_registered_device * 97 + cfg80211_get_dev_from_info(struct genl_info *info) 98 + { 99 + struct cfg80211_registered_device *drv; 100 + 101 + mutex_lock(&cfg80211_drv_mutex); 102 + drv = __cfg80211_drv_from_info(info); 103 + 104 + /* if it is not an error we grab the lock on 105 + * it to assure it won't be going away while 106 + * we operate on it */ 107 + if (!IS_ERR(drv)) 108 + mutex_lock(&drv->mtx); 109 + 110 + mutex_unlock(&cfg80211_drv_mutex); 111 + 112 + return drv; 113 + } 114 + 115 + struct cfg80211_registered_device * 116 + cfg80211_get_dev_from_ifindex(int ifindex) 117 + { 118 + struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV); 119 + struct net_device *dev; 120 + 121 + mutex_lock(&cfg80211_drv_mutex); 122 + dev = dev_get_by_index(&init_net, ifindex); 123 + if (!dev) 124 + goto out; 125 + if (dev->ieee80211_ptr) { 126 + drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy); 127 + mutex_lock(&drv->mtx); 128 + } else 129 + drv = ERR_PTR(-ENODEV); 130 + dev_put(dev); 131 + out: 132 + mutex_unlock(&cfg80211_drv_mutex); 133 + return drv; 134 + } 135 + 136 + void cfg80211_put_dev(struct cfg80211_registered_device *drv) 137 + { 138 + BUG_ON(IS_ERR(drv)); 139 + mutex_unlock(&drv->mtx); 140 + } 141 + 142 + int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, 143 + char *newname) 144 + { 145 + int idx, taken = -1, result, digits; 146 + 147 + /* prohibit calling the thing phy%d when %d is not its number */ 148 + sscanf(newname, PHY_NAME "%d%n", &idx, &taken); 149 + if (taken == strlen(newname) && idx != rdev->idx) { 150 + /* count number of places needed to print idx */ 151 + digits = 1; 152 + while (idx /= 10) 153 + digits++; 154 + /* 155 + * deny the name if it is phy<idx> where <idx> is printed 156 + * without leading zeroes. taken == strlen(newname) here 157 + */ 158 + if (taken == strlen(PHY_NAME) + digits) 159 + return -EINVAL; 160 + } 161 + 162 + /* this will check for collisions */ 163 + result = device_rename(&rdev->wiphy.dev, newname); 164 + if (result) 165 + return result; 166 + 167 + if (!debugfs_rename(rdev->wiphy.debugfsdir->d_parent, 168 + rdev->wiphy.debugfsdir, 169 + rdev->wiphy.debugfsdir->d_parent, 170 + newname)) 171 + printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", 172 + newname); 173 + 174 + nl80211_notify_dev_rename(rdev); 175 + 176 + return 0; 177 + } 39 178 40 179 /* exported functions */ 41 180 ··· 345 204 if (err) 346 205 goto out_fail_notifier; 347 206 207 + err = nl80211_init(); 208 + if (err) 209 + goto out_fail_nl80211; 210 + 348 211 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); 349 212 350 213 return 0; 351 214 215 + out_fail_nl80211: 216 + unregister_netdevice_notifier(&cfg80211_netdev_notifier); 352 217 out_fail_notifier: 353 218 wiphy_sysfs_exit(); 354 219 out_fail_sysfs: ··· 365 218 static void cfg80211_exit(void) 366 219 { 367 220 debugfs_remove(ieee80211_debugfs_dir); 221 + nl80211_exit(); 368 222 unregister_netdevice_notifier(&cfg80211_netdev_notifier); 369 223 wiphy_sysfs_exit(); 370 224 }
+32
net/wireless/core.h
··· 43 43 extern struct mutex cfg80211_drv_mutex; 44 44 extern struct list_head cfg80211_drv_list; 45 45 46 + /* 47 + * This function returns a pointer to the driver 48 + * that the genl_info item that is passed refers to. 49 + * If successful, it returns non-NULL and also locks 50 + * the driver's mutex! 51 + * 52 + * This means that you need to call cfg80211_put_dev() 53 + * before being allowed to acquire &cfg80211_drv_mutex! 54 + * 55 + * This is necessary because we need to lock the global 56 + * mutex to get an item off the list safely, and then 57 + * we lock the drv mutex so it doesn't go away under us. 58 + * 59 + * We don't want to keep cfg80211_drv_mutex locked 60 + * for all the time in order to allow requests on 61 + * other interfaces to go through at the same time. 62 + * 63 + * The result of this can be a PTR_ERR and hence must 64 + * be checked with IS_ERR() for errors. 65 + */ 66 + extern struct cfg80211_registered_device * 67 + cfg80211_get_dev_from_info(struct genl_info *info); 68 + 69 + /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ 70 + extern struct cfg80211_registered_device * 71 + cfg80211_get_dev_from_ifindex(int ifindex); 72 + 73 + extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); 74 + 46 75 /* free object */ 47 76 extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); 77 + 78 + extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, 79 + char *newname); 48 80 49 81 #endif /* __NET_WIRELESS_CORE_H */
+431
net/wireless/nl80211.c
··· 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 */ 23 + static 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 */ 32 + static 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 */ 56 + static 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 */ 67 + static 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 + 76 + static 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 + 93 + static 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 + 115 + static 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 + 142 + static 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 + 161 + static 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 + 179 + static 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 + 213 + static 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 + 244 + static 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 + 278 + static 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 + 312 + static 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 + 338 + static 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 */ 380 + static struct genl_multicast_group nl80211_config_mcgrp = { 381 + .name = "config", 382 + }; 383 + 384 + /* notification functions */ 385 + 386 + void 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 + 404 + int 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 + 428 + void nl80211_exit(void) 429 + { 430 + genl_unregister_family(&nl80211_fam); 431 + }
+24
net/wireless/nl80211.h
··· 1 + #ifndef __NET_WIRELESS_NL80211_H 2 + #define __NET_WIRELESS_NL80211_H 3 + 4 + #include "core.h" 5 + 6 + #ifdef CONFIG_NL80211 7 + extern int nl80211_init(void); 8 + extern void nl80211_exit(void); 9 + extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); 10 + #else 11 + static inline int nl80211_init(void) 12 + { 13 + return 0; 14 + } 15 + static inline void nl80211_exit(void) 16 + { 17 + } 18 + static inline void nl80211_notify_dev_rename( 19 + struct cfg80211_registered_device *rdev) 20 + { 21 + } 22 + #endif /* CONFIG_NL80211 */ 23 + 24 + #endif /* __NET_WIRELESS_NL80211_H */