irnetlink.c 3.3 KB
Newer Older
S
Samuel Ortiz 已提交
1 2 3
/*
 * IrDA netlink layer, for stack configuration.
 *
4
 * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
S
Samuel Ortiz 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
 *
 * Partly based on the 802.11 nelink implementation
 * (see net/wireless/nl80211.c) which is:
 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/socket.h>
#include <linux/irda.h>
#include <net/sock.h>
#include <net/irda/irda.h>
#include <net/irda/irlap.h>
#include <net/genetlink.h>



static struct genl_family irda_nl_family = {
	.id = GENL_ID_GENERATE,
	.name = IRDA_NL_NAME,
	.hdrsize = 0,
	.version = IRDA_NL_VERSION,
	.maxattr = IRDA_NL_CMD_MAX,
};

static struct net_device * ifname_to_netdev(struct genl_info *info)
{
	char * ifname;

	if (!info->attrs[IRDA_NL_ATTR_IFNAME])
		return NULL;

	ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]);

	IRDA_DEBUG(5, "%s(): Looking for %s\n", __FUNCTION__, ifname);

	return dev_get_by_name(ifname);
}

static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info)
{
	struct net_device * dev;
	struct irlap_cb * irlap;
	u32 mode;

	if (!info->attrs[IRDA_NL_ATTR_MODE])
		return -EINVAL;

	mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]);

	IRDA_DEBUG(5, "%s(): Switching to mode: %d\n", __FUNCTION__, mode);

	dev = ifname_to_netdev(info);
	if (!dev)
		return -ENODEV;

	irlap = (struct irlap_cb *)dev->atalk_ptr;
	if (!irlap) {
		dev_put(dev);
		return -ENODEV;
	}

	irlap->mode = mode;

	dev_put(dev);

	return 0;
}

static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info)
{
	struct net_device * dev;
	struct irlap_cb * irlap;
	struct sk_buff *msg;
	void *hdr;
	int ret = -ENOBUFS;

	dev = ifname_to_netdev(info);
	if (!dev)
		return -ENODEV;

	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	if (!msg) {
		dev_put(dev);
		return -ENOMEM;
	}

	irlap = (struct irlap_cb *)dev->atalk_ptr;
	if (!irlap) {
		ret = -ENODEV;
		goto err_out;
	}

	hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
			  &irda_nl_family, 0,  IRDA_NL_CMD_GET_MODE);
	if (IS_ERR(hdr)) {
		ret = PTR_ERR(hdr);
		goto err_out;
	}

	if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME,
109
			  dev->name))
S
Samuel Ortiz 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
		goto err_out;

	if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode))
		goto err_out;

	genlmsg_end(msg, hdr);

	return genlmsg_unicast(msg, info->snd_pid);

 err_out:
	nlmsg_free(msg);
	dev_put(dev);

	return ret;
}

static struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = {
	[IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING,
				  .len = IFNAMSIZ-1 },
	[IRDA_NL_ATTR_MODE] = { .type = NLA_U32 },
};

static struct genl_ops irda_nl_ops[] = {
	{
		.cmd = IRDA_NL_CMD_SET_MODE,
		.doit = irda_nl_set_mode,
		.policy = irda_nl_policy,
		.flags = GENL_ADMIN_PERM,
	},
	{
		.cmd = IRDA_NL_CMD_GET_MODE,
		.doit = irda_nl_get_mode,
		.policy = irda_nl_policy,
		/* can be retrieved by unprivileged users */
	},

};

int irda_nl_register(void)
{
	int err, i;

	err = genl_register_family(&irda_nl_family);
	if (err)
		return err;

	for (i = 0; i < ARRAY_SIZE(irda_nl_ops); i++) {
		err = genl_register_ops(&irda_nl_family, &irda_nl_ops[i]);
		if (err)
			goto err_out;
	}
	return 0;
 err_out:
	genl_unregister_family(&irda_nl_family);
	return err;
}

void irda_nl_unregister(void)
{
	genl_unregister_family(&irda_nl_family);
}