⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nl80211.c

📁 linux 内核源代码
💻 C
字号:
/* * This is the new netlink-based wireless configuration interface. * * Copyright 2006, 2007	Johannes Berg <johannes@sipsolutions.net> */#include <linux/if.h>#include <linux/module.h>#include <linux/err.h>#include <linux/mutex.h>#include <linux/list.h>#include <linux/if_ether.h>#include <linux/ieee80211.h>#include <linux/nl80211.h>#include <linux/rtnetlink.h>#include <linux/netlink.h>#include <net/genetlink.h>#include <net/cfg80211.h>#include "core.h"#include "nl80211.h"/* the netlink family */static struct genl_family nl80211_fam = {	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */	.name = "nl80211",	/* have users key off the name instead */	.hdrsize = 0,		/* no private header */	.version = 1,		/* no particular meaning now */	.maxattr = NL80211_ATTR_MAX,};/* internal helper: get drv and dev */static int get_drv_dev_by_info_ifindex(struct genl_info *info,				       struct cfg80211_registered_device **drv,				       struct net_device **dev){	int ifindex;	if (!info->attrs[NL80211_ATTR_IFINDEX])		return -EINVAL;	ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);	*dev = dev_get_by_index(&init_net, ifindex);	if (!*dev)		return -ENODEV;	*drv = cfg80211_get_dev_from_ifindex(ifindex);	if (IS_ERR(*drv)) {		dev_put(*dev);		return PTR_ERR(*drv);	}	return 0;}/* policy for the attributes */static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },	[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,				      .len = BUS_ID_SIZE-1 },	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },};/* message building helper */static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,				   int flags, u8 cmd){	/* since there is no private header just add the generic one */	return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);}/* netlink command implementations */static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,			      struct cfg80211_registered_device *dev){	void *hdr;	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);	if (!hdr)		return -1;	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));	return genlmsg_end(msg, hdr); nla_put_failure:	return genlmsg_cancel(msg, hdr);}static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb){	int idx = 0;	int start = cb->args[0];	struct cfg80211_registered_device *dev;	mutex_lock(&cfg80211_drv_mutex);	list_for_each_entry(dev, &cfg80211_drv_list, list) {		if (++idx < start)			continue;		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,				       cb->nlh->nlmsg_seq, NLM_F_MULTI,				       dev) < 0)			break;	}	mutex_unlock(&cfg80211_drv_mutex);	cb->args[0] = idx;	return skb->len;}static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info){	struct sk_buff *msg;	struct cfg80211_registered_device *dev;	dev = cfg80211_get_dev_from_info(info);	if (IS_ERR(dev))		return PTR_ERR(dev);	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);	if (!msg)		goto out_err;	if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)		goto out_free;	cfg80211_put_dev(dev);	return genlmsg_unicast(msg, info->snd_pid); out_free:	nlmsg_free(msg); out_err:	cfg80211_put_dev(dev);	return -ENOBUFS;}static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info){	struct cfg80211_registered_device *rdev;	int result;	if (!info->attrs[NL80211_ATTR_WIPHY_NAME])		return -EINVAL;	rdev = cfg80211_get_dev_from_info(info);	if (IS_ERR(rdev))		return PTR_ERR(rdev);	result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));	cfg80211_put_dev(rdev);	return result;}static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,			      struct net_device *dev){	void *hdr;	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);	if (!hdr)		return -1;	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);	/* TODO: interface type */	return genlmsg_end(msg, hdr); nla_put_failure:	return genlmsg_cancel(msg, hdr);}static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb){	int wp_idx = 0;	int if_idx = 0;	int wp_start = cb->args[0];	int if_start = cb->args[1];	struct cfg80211_registered_device *dev;	struct wireless_dev *wdev;	mutex_lock(&cfg80211_drv_mutex);	list_for_each_entry(dev, &cfg80211_drv_list, list) {		if (++wp_idx < wp_start)			continue;		if_idx = 0;		mutex_lock(&dev->devlist_mtx);		list_for_each_entry(wdev, &dev->netdev_list, list) {			if (++if_idx < if_start)				continue;			if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,					       cb->nlh->nlmsg_seq, NLM_F_MULTI,					       wdev->netdev) < 0)				break;		}		mutex_unlock(&dev->devlist_mtx);	}	mutex_unlock(&cfg80211_drv_mutex);	cb->args[0] = wp_idx;	cb->args[1] = if_idx;	return skb->len;}static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info){	struct sk_buff *msg;	struct cfg80211_registered_device *dev;	struct net_device *netdev;	int err;	err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);	if (err)		return err;	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);	if (!msg)		goto out_err;	if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)		goto out_free;	dev_put(netdev);	cfg80211_put_dev(dev);	return genlmsg_unicast(msg, info->snd_pid); out_free:	nlmsg_free(msg); out_err:	dev_put(netdev);	cfg80211_put_dev(dev);	return -ENOBUFS;}static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info){	struct cfg80211_registered_device *drv;	int err, ifindex;	enum nl80211_iftype type;	struct net_device *dev;	if (info->attrs[NL80211_ATTR_IFTYPE]) {		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);		if (type > NL80211_IFTYPE_MAX)			return -EINVAL;	} else		return -EINVAL;	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);	if (err)		return err;	ifindex = dev->ifindex;	dev_put(dev);	if (!drv->ops->change_virtual_intf) {		err = -EOPNOTSUPP;		goto unlock;	}	rtnl_lock();	err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);	rtnl_unlock(); unlock:	cfg80211_put_dev(drv);	return err;}static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info){	struct cfg80211_registered_device *drv;	int err;	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;	if (!info->attrs[NL80211_ATTR_IFNAME])		return -EINVAL;	if (info->attrs[NL80211_ATTR_IFTYPE]) {		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);		if (type > NL80211_IFTYPE_MAX)			return -EINVAL;	}	drv = cfg80211_get_dev_from_info(info);	if (IS_ERR(drv))		return PTR_ERR(drv);	if (!drv->ops->add_virtual_intf) {		err = -EOPNOTSUPP;		goto unlock;	}	rtnl_lock();	err = drv->ops->add_virtual_intf(&drv->wiphy,		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);	rtnl_unlock(); unlock:	cfg80211_put_dev(drv);	return err;}static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info){	struct cfg80211_registered_device *drv;	int ifindex, err;	struct net_device *dev;	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);	if (err)		return err;	ifindex = dev->ifindex;	dev_put(dev);	if (!drv->ops->del_virtual_intf) {		err = -EOPNOTSUPP;		goto out;	}	rtnl_lock();	err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);	rtnl_unlock(); out:	cfg80211_put_dev(drv);	return err;}static struct genl_ops nl80211_ops[] = {	{		.cmd = NL80211_CMD_GET_WIPHY,		.doit = nl80211_get_wiphy,		.dumpit = nl80211_dump_wiphy,		.policy = nl80211_policy,		/* can be retrieved by unprivileged users */	},	{		.cmd = NL80211_CMD_SET_WIPHY,		.doit = nl80211_set_wiphy,		.policy = nl80211_policy,		.flags = GENL_ADMIN_PERM,	},	{		.cmd = NL80211_CMD_GET_INTERFACE,		.doit = nl80211_get_interface,		.dumpit = nl80211_dump_interface,		.policy = nl80211_policy,		/* can be retrieved by unprivileged users */	},	{		.cmd = NL80211_CMD_SET_INTERFACE,		.doit = nl80211_set_interface,		.policy = nl80211_policy,		.flags = GENL_ADMIN_PERM,	},	{		.cmd = NL80211_CMD_NEW_INTERFACE,		.doit = nl80211_new_interface,		.policy = nl80211_policy,		.flags = GENL_ADMIN_PERM,	},	{		.cmd = NL80211_CMD_DEL_INTERFACE,		.doit = nl80211_del_interface,		.policy = nl80211_policy,		.flags = GENL_ADMIN_PERM,	},};/* multicast groups */static struct genl_multicast_group nl80211_config_mcgrp = {	.name = "config",};/* notification functions */void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev){	struct sk_buff *msg;	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);	if (!msg)		return;	if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {		nlmsg_free(msg);		return;	}	genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);}/* initialisation/exit functions */int nl80211_init(void){	int err, i;	err = genl_register_family(&nl80211_fam);	if (err)		return err;	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);		if (err)			goto err_out;	}	err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);	if (err)		goto err_out;	return 0; err_out:	genl_unregister_family(&nl80211_fam);	return err;}void nl80211_exit(void){	genl_unregister_family(&nl80211_fam);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -