Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

5
net/netlink/Makefile Normal file
View File

@@ -0,0 +1,5 @@
#
# Makefile for the netlink driver.
#
obj-y := af_netlink.o attr.o genetlink.o

1832
net/netlink/af_netlink.c Normal file

File diff suppressed because it is too large Load Diff

431
net/netlink/attr.c Normal file
View File

@@ -0,0 +1,431 @@
/*
* NETLINK Netlink attributes
*
* Authors: Thomas Graf <tgraf@suug.ch>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/types.h>
#include <net/netlink.h>
static u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = {
[NLA_U8] = sizeof(u8),
[NLA_U16] = sizeof(u16),
[NLA_U32] = sizeof(u32),
[NLA_U64] = sizeof(u64),
[NLA_NESTED] = NLA_HDRLEN,
};
static int validate_nla(struct nlattr *nla, int maxtype,
struct nla_policy *policy)
{
struct nla_policy *pt;
int minlen = 0, attrlen = nla_len(nla);
if (nla->nla_type <= 0 || nla->nla_type > maxtype)
return 0;
pt = &policy[nla->nla_type];
BUG_ON(pt->type > NLA_TYPE_MAX);
switch (pt->type) {
case NLA_FLAG:
if (attrlen > 0)
return -ERANGE;
break;
case NLA_NUL_STRING:
if (pt->len)
minlen = min_t(int, attrlen, pt->len + 1);
else
minlen = attrlen;
if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL)
return -EINVAL;
/* fall through */
case NLA_STRING:
if (attrlen < 1)
return -ERANGE;
if (pt->len) {
char *buf = nla_data(nla);
if (buf[attrlen - 1] == '\0')
attrlen--;
if (attrlen > pt->len)
return -ERANGE;
}
break;
default:
if (pt->len)
minlen = pt->len;
else if (pt->type != NLA_UNSPEC)
minlen = nla_attr_minlen[pt->type];
if (attrlen < minlen)
return -ERANGE;
}
return 0;
}
/**
* nla_validate - Validate a stream of attributes
* @head: head of attribute stream
* @len: length of attribute stream
* @maxtype: maximum attribute type to be expected
* @policy: validation policy
*
* Validates all attributes in the specified attribute stream against the
* specified policy. Attributes with a type exceeding maxtype will be
* ignored. See documenation of struct nla_policy for more details.
*
* Returns 0 on success or a negative error code.
*/
int nla_validate(struct nlattr *head, int len, int maxtype,
struct nla_policy *policy)
{
struct nlattr *nla;
int rem, err;
nla_for_each_attr(nla, head, len, rem) {
err = validate_nla(nla, maxtype, policy);
if (err < 0)
goto errout;
}
err = 0;
errout:
return err;
}
/**
* nla_parse - Parse a stream of attributes into a tb buffer
* @tb: destination array with maxtype+1 elements
* @maxtype: maximum attribute type to be expected
* @head: head of attribute stream
* @len: length of attribute stream
*
* Parses a stream of attributes and stores a pointer to each attribute in
* the tb array accessable via the attribute type. Attributes with a type
* exceeding maxtype will be silently ignored for backwards compatibility
* reasons. policy may be set to NULL if no validation is required.
*
* Returns 0 on success or a negative error code.
*/
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
struct nla_policy *policy)
{
struct nlattr *nla;
int rem, err;
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
nla_for_each_attr(nla, head, len, rem) {
u16 type = nla->nla_type;
if (type > 0 && type <= maxtype) {
if (policy) {
err = validate_nla(nla, maxtype, policy);
if (err < 0)
goto errout;
}
tb[type] = nla;
}
}
if (unlikely(rem > 0))
printk(KERN_WARNING "netlink: %d bytes leftover after parsing "
"attributes.\n", rem);
err = 0;
errout:
return err;
}
/**
* nla_find - Find a specific attribute in a stream of attributes
* @head: head of attribute stream
* @len: length of attribute stream
* @attrtype: type of attribute to look for
*
* Returns the first attribute in the stream matching the specified type.
*/
struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
{
struct nlattr *nla;
int rem;
nla_for_each_attr(nla, head, len, rem)
if (nla->nla_type == attrtype)
return nla;
return NULL;
}
/**
* nla_strlcpy - Copy string attribute payload into a sized buffer
* @dst: where to copy the string to
* @src: attribute to copy the string from
* @dstsize: size of destination buffer
*
* Copies at most dstsize - 1 bytes into the destination buffer.
* The result is always a valid NUL-terminated string. Unlike
* strlcpy the destination buffer is always padded out.
*
* Returns the length of the source buffer.
*/
size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
{
size_t srclen = nla_len(nla);
char *src = nla_data(nla);
if (srclen > 0 && src[srclen - 1] == '\0')
srclen--;
if (dstsize > 0) {
size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
memset(dst, 0, dstsize);
memcpy(dst, src, len);
}
return srclen;
}
/**
* nla_memcpy - Copy a netlink attribute into another memory area
* @dest: where to copy to memcpy
* @src: netlink attribute to copy from
* @count: size of the destination area
*
* Note: The number of bytes copied is limited by the length of
* attribute's payload. memcpy
*
* Returns the number of bytes copied.
*/
int nla_memcpy(void *dest, struct nlattr *src, int count)
{
int minlen = min_t(int, count, nla_len(src));
memcpy(dest, nla_data(src), minlen);
return minlen;
}
/**
* nla_memcmp - Compare an attribute with sized memory area
* @nla: netlink attribute
* @data: memory area
* @size: size of memory area
*/
int nla_memcmp(const struct nlattr *nla, const void *data,
size_t size)
{
int d = nla_len(nla) - size;
if (d == 0)
d = memcmp(nla_data(nla), data, size);
return d;
}
/**
* nla_strcmp - Compare a string attribute against a string
* @nla: netlink string attribute
* @str: another string
*/
int nla_strcmp(const struct nlattr *nla, const char *str)
{
int len = strlen(str) + 1;
int d = nla_len(nla) - len;
if (d == 0)
d = memcmp(nla_data(nla), str, len);
return d;
}
/**
* __nla_reserve - reserve room for attribute on the skb
* @skb: socket buffer to reserve room on
* @attrtype: attribute type
* @attrlen: length of attribute payload
*
* Adds a netlink attribute header to a socket buffer and reserves
* room for the payload but does not copy it.
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute header and payload.
*/
struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
{
struct nlattr *nla;
nla = (struct nlattr *) skb_put(skb, nla_total_size(attrlen));
nla->nla_type = attrtype;
nla->nla_len = nla_attr_size(attrlen);
memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
return nla;
}
/**
* __nla_reserve_nohdr - reserve room for attribute without header
* @skb: socket buffer to reserve room on
* @attrlen: length of attribute payload
*
* Reserves room for attribute payload without a header.
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the payload.
*/
void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
{
void *start;
start = skb_put(skb, NLA_ALIGN(attrlen));
memset(start, 0, NLA_ALIGN(attrlen));
return start;
}
/**
* nla_reserve - reserve room for attribute on the skb
* @skb: socket buffer to reserve room on
* @attrtype: attribute type
* @attrlen: length of attribute payload
*
* Adds a netlink attribute header to a socket buffer and reserves
* room for the payload but does not copy it.
*
* Returns NULL if the tailroom of the skb is insufficient to store
* the attribute header and payload.
*/
struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen)
{
if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen)))
return NULL;
return __nla_reserve(skb, attrtype, attrlen);
}
/**
* nla_reserve - reserve room for attribute without header
* @skb: socket buffer to reserve room on
* @len: length of attribute payload
*
* Reserves room for attribute payload without a header.
*
* Returns NULL if the tailroom of the skb is insufficient to store
* the attribute payload.
*/
void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen)
{
if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen)))
return NULL;
return __nla_reserve_nohdr(skb, attrlen);
}
/**
* __nla_put - Add a netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to
* @attrtype: attribute type
* @attrlen: length of attribute payload
* @data: head of attribute payload
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute header and payload.
*/
void __nla_put(struct sk_buff *skb, int attrtype, int attrlen,
const void *data)
{
struct nlattr *nla;
nla = __nla_reserve(skb, attrtype, attrlen);
memcpy(nla_data(nla), data, attrlen);
}
/**
* __nla_put_nohdr - Add a netlink attribute without header
* @skb: socket buffer to add attribute to
* @attrlen: length of attribute payload
* @data: head of attribute payload
*
* The caller is responsible to ensure that the skb provides enough
* tailroom for the attribute payload.
*/
void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
{
void *start;
start = __nla_reserve_nohdr(skb, attrlen);
memcpy(start, data, attrlen);
}
/**
* nla_put - Add a netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to
* @attrtype: attribute type
* @attrlen: length of attribute payload
* @data: head of attribute payload
*
* Returns -1 if the tailroom of the skb is insufficient to store
* the attribute header and payload.
*/
int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
{
if (unlikely(skb_tailroom(skb) < nla_total_size(attrlen)))
return -1;
__nla_put(skb, attrtype, attrlen, data);
return 0;
}
/**
* nla_put_nohdr - Add a netlink attribute without header
* @skb: socket buffer to add attribute to
* @attrlen: length of attribute payload
* @data: head of attribute payload
*
* Returns -1 if the tailroom of the skb is insufficient to store
* the attribute payload.
*/
int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data)
{
if (unlikely(skb_tailroom(skb) < NLA_ALIGN(attrlen)))
return -1;
__nla_put_nohdr(skb, attrlen, data);
return 0;
}
EXPORT_SYMBOL(nla_validate);
EXPORT_SYMBOL(nla_parse);
EXPORT_SYMBOL(nla_find);
EXPORT_SYMBOL(nla_strlcpy);
EXPORT_SYMBOL(__nla_reserve);
EXPORT_SYMBOL(__nla_reserve_nohdr);
EXPORT_SYMBOL(nla_reserve);
EXPORT_SYMBOL(nla_reserve_nohdr);
EXPORT_SYMBOL(__nla_put);
EXPORT_SYMBOL(__nla_put_nohdr);
EXPORT_SYMBOL(nla_put);
EXPORT_SYMBOL(nla_put_nohdr);
EXPORT_SYMBOL(nla_memcpy);
EXPORT_SYMBOL(nla_memcmp);
EXPORT_SYMBOL(nla_strcmp);

607
net/netlink/genetlink.c Normal file
View File

@@ -0,0 +1,607 @@
/*
* NETLINK Generic Netlink Family
*
* Authors: Jamal Hadi Salim
* Thomas Graf <tgraf@suug.ch>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/mutex.h>
#include <net/sock.h>
#include <net/genetlink.h>
struct sock *genl_sock = NULL;
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
static void genl_lock(void)
{
mutex_lock(&genl_mutex);
}
static int genl_trylock(void)
{
return !mutex_trylock(&genl_mutex);
}
static void genl_unlock(void)
{
mutex_unlock(&genl_mutex);
if (genl_sock && genl_sock->sk_receive_queue.qlen)
genl_sock->sk_data_ready(genl_sock, 0);
}
#define GENL_FAM_TAB_SIZE 16
#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)
static struct list_head family_ht[GENL_FAM_TAB_SIZE];
static int genl_ctrl_event(int event, void *data);
static inline unsigned int genl_family_hash(unsigned int id)
{
return id & GENL_FAM_TAB_MASK;
}
static inline struct list_head *genl_family_chain(unsigned int id)
{
return &family_ht[genl_family_hash(id)];
}
static struct genl_family *genl_family_find_byid(unsigned int id)
{
struct genl_family *f;
list_for_each_entry(f, genl_family_chain(id), family_list)
if (f->id == id)
return f;
return NULL;
}
static struct genl_family *genl_family_find_byname(char *name)
{
struct genl_family *f;
int i;
for (i = 0; i < GENL_FAM_TAB_SIZE; i++)
list_for_each_entry(f, genl_family_chain(i), family_list)
if (strcmp(f->name, name) == 0)
return f;
return NULL;
}
static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
{
struct genl_ops *ops;
list_for_each_entry(ops, &family->ops_list, ops_list)
if (ops->cmd == cmd)
return ops;
return NULL;
}
/* Of course we are going to have problems once we hit
* 2^16 alive types, but that can only happen by year 2K
*/
static inline u16 genl_generate_id(void)
{
static u16 id_gen_idx;
int overflowed = 0;
do {
if (id_gen_idx == 0)
id_gen_idx = GENL_MIN_ID;
if (++id_gen_idx > GENL_MAX_ID) {
if (!overflowed) {
overflowed = 1;
id_gen_idx = 0;
continue;
} else
return 0;
}
} while (genl_family_find_byid(id_gen_idx));
return id_gen_idx;
}
/**
* genl_register_ops - register generic netlink operations
* @family: generic netlink family
* @ops: operations to be registered
*
* Registers the specified operations and assigns them to the specified
* family. Either a doit or dumpit callback must be specified or the
* operation will fail. Only one operation structure per command
* identifier may be registered.
*
* See include/net/genetlink.h for more documenation on the operations
* structure.
*
* Returns 0 on success or a negative error code.
*/
int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
{
int err = -EINVAL;
if (ops->dumpit == NULL && ops->doit == NULL)
goto errout;
if (genl_get_cmd(ops->cmd, family)) {
err = -EEXIST;
goto errout;
}
if (ops->dumpit)
ops->flags |= GENL_CMD_CAP_DUMP;
if (ops->doit)
ops->flags |= GENL_CMD_CAP_DO;
if (ops->policy)
ops->flags |= GENL_CMD_CAP_HASPOL;
genl_lock();
list_add_tail(&ops->ops_list, &family->ops_list);
genl_unlock();
genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
err = 0;
errout:
return err;
}
/**
* genl_unregister_ops - unregister generic netlink operations
* @family: generic netlink family
* @ops: operations to be unregistered
*
* Unregisters the specified operations and unassigns them from the
* specified family. The operation blocks until the current message
* processing has finished and doesn't start again until the
* unregister process has finished.
*
* Note: It is not necessary to unregister all operations before
* unregistering the family, unregistering the family will cause
* all assigned operations to be unregistered automatically.
*
* Returns 0 on success or a negative error code.
*/
int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops)
{
struct genl_ops *rc;
genl_lock();
list_for_each_entry(rc, &family->ops_list, ops_list) {
if (rc == ops) {
list_del(&ops->ops_list);
genl_unlock();
genl_ctrl_event(CTRL_CMD_DELOPS, ops);
return 0;
}
}
genl_unlock();
return -ENOENT;
}
/**
* genl_register_family - register a generic netlink family
* @family: generic netlink family
*
* Registers the specified family after validating it first. Only one
* family may be registered with the same family name or identifier.
* The family id may equal GENL_ID_GENERATE causing an unique id to
* be automatically generated and assigned.
*
* Return 0 on success or a negative error code.
*/
int genl_register_family(struct genl_family *family)
{
int err = -EINVAL;
if (family->id && family->id < GENL_MIN_ID)
goto errout;
if (family->id > GENL_MAX_ID)
goto errout;
INIT_LIST_HEAD(&family->ops_list);
genl_lock();
if (genl_family_find_byname(family->name)) {
err = -EEXIST;
goto errout_locked;
}
if (genl_family_find_byid(family->id)) {
err = -EEXIST;
goto errout_locked;
}
if (family->id == GENL_ID_GENERATE) {
u16 newid = genl_generate_id();
if (!newid) {
err = -ENOMEM;
goto errout_locked;
}
family->id = newid;
}
if (family->maxattr) {
family->attrbuf = kmalloc((family->maxattr+1) *
sizeof(struct nlattr *), GFP_KERNEL);
if (family->attrbuf == NULL) {
err = -ENOMEM;
goto errout_locked;
}
} else
family->attrbuf = NULL;
list_add_tail(&family->family_list, genl_family_chain(family->id));
genl_unlock();
genl_ctrl_event(CTRL_CMD_NEWFAMILY, family);
return 0;
errout_locked:
genl_unlock();
errout:
return err;
}
/**
* genl_unregister_family - unregister generic netlink family
* @family: generic netlink family
*
* Unregisters the specified family.
*
* Returns 0 on success or a negative error code.
*/
int genl_unregister_family(struct genl_family *family)
{
struct genl_family *rc;
genl_lock();
list_for_each_entry(rc, genl_family_chain(family->id), family_list) {
if (family->id != rc->id || strcmp(rc->name, family->name))
continue;
list_del(&rc->family_list);
INIT_LIST_HEAD(&family->ops_list);
genl_unlock();
kfree(family->attrbuf);
genl_ctrl_event(CTRL_CMD_DELFAMILY, family);
return 0;
}
genl_unlock();
return -ENOENT;
}
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
int *errp)
{
struct genl_ops *ops;
struct genl_family *family;
struct genl_info info;
struct genlmsghdr *hdr = nlmsg_data(nlh);
int hdrlen, err = -EINVAL;
if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
goto ignore;
if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
goto ignore;
family = genl_family_find_byid(nlh->nlmsg_type);
if (family == NULL) {
err = -ENOENT;
goto errout;
}
hdrlen = GENL_HDRLEN + family->hdrsize;
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
goto errout;
ops = genl_get_cmd(hdr->cmd, family);
if (ops == NULL) {
err = -EOPNOTSUPP;
goto errout;
}
if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) {
err = -EPERM;
goto errout;
}
if (nlh->nlmsg_flags & NLM_F_DUMP) {
if (ops->dumpit == NULL) {
err = -EOPNOTSUPP;
goto errout;
}
*errp = err = netlink_dump_start(genl_sock, skb, nlh,
ops->dumpit, ops->done);
if (err == 0)
skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len),
skb->len));
return -1;
}
if (ops->doit == NULL) {
err = -EOPNOTSUPP;
goto errout;
}
if (family->attrbuf) {
err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr,
ops->policy);
if (err < 0)
goto errout;
}
info.snd_seq = nlh->nlmsg_seq;
info.snd_pid = NETLINK_CB(skb).pid;
info.nlhdr = nlh;
info.genlhdr = nlmsg_data(nlh);
info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
info.attrs = family->attrbuf;
*errp = err = ops->doit(skb, &info);
return err;
ignore:
return 0;
errout:
*errp = err;
return -1;
}
static void genl_rcv(struct sock *sk, int len)
{
unsigned int qlen = 0;
do {
if (genl_trylock())
return;
netlink_run_queue(sk, &qlen, genl_rcv_msg);
genl_unlock();
} while (qlen && genl_sock && genl_sock->sk_receive_queue.qlen);
}
/**************************************************************************
* Controller
**************************************************************************/
static struct genl_family genl_ctrl = {
.id = GENL_ID_CTRL,
.name = "nlctrl",
.version = 0x2,
.maxattr = CTRL_ATTR_MAX,
};
static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq,
u32 flags, struct sk_buff *skb, u8 cmd)
{
void *hdr;
hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd);
if (hdr == NULL)
return -1;
NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, family->name);
NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, family->id);
NLA_PUT_U32(skb, CTRL_ATTR_VERSION, family->version);
NLA_PUT_U32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize);
NLA_PUT_U32(skb, CTRL_ATTR_MAXATTR, family->maxattr);
if (!list_empty(&family->ops_list)) {
struct nlattr *nla_ops;
struct genl_ops *ops;
int idx = 1;
nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
if (nla_ops == NULL)
goto nla_put_failure;
list_for_each_entry(ops, &family->ops_list, ops_list) {
struct nlattr *nest;
nest = nla_nest_start(skb, idx++);
if (nest == NULL)
goto nla_put_failure;
NLA_PUT_U32(skb, CTRL_ATTR_OP_ID, ops->cmd);
NLA_PUT_U32(skb, CTRL_ATTR_OP_FLAGS, ops->flags);
nla_nest_end(skb, nest);
}
nla_nest_end(skb, nla_ops);
}
return genlmsg_end(skb, hdr);
nla_put_failure:
return genlmsg_cancel(skb, hdr);
}
static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
{
int i, n = 0;
struct genl_family *rt;
int chains_to_skip = cb->args[0];
int fams_to_skip = cb->args[1];
if (chains_to_skip != 0)
genl_lock();
for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
if (i < chains_to_skip)
continue;
n = 0;
list_for_each_entry(rt, genl_family_chain(i), family_list) {
if (++n < fams_to_skip)
continue;
if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
skb, CTRL_CMD_NEWFAMILY) < 0)
goto errout;
}
fams_to_skip = 0;
}
errout:
if (chains_to_skip != 0)
genl_unlock();
cb->args[0] = i;
cb->args[1] = n;
return skb->len;
}
static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
int seq, u8 cmd)
{
struct sk_buff *skb;
int err;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (skb == NULL)
return ERR_PTR(-ENOBUFS);
err = ctrl_fill_info(family, pid, seq, 0, skb, cmd);
if (err < 0) {
nlmsg_free(skb);
return ERR_PTR(err);
}
return skb;
}
static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] __read_mostly = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING,
.len = GENL_NAMSIZ - 1 },
};
static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
{
struct sk_buff *msg;
struct genl_family *res = NULL;
int err = -EINVAL;
if (info->attrs[CTRL_ATTR_FAMILY_ID]) {
u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]);
res = genl_family_find_byid(id);
}
if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
char *name;
name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
res = genl_family_find_byname(name);
}
if (res == NULL) {
err = -ENOENT;
goto errout;
}
msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq,
CTRL_CMD_NEWFAMILY);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto errout;
}
err = genlmsg_reply(msg, info);
errout:
return err;
}
static int genl_ctrl_event(int event, void *data)
{
struct sk_buff *msg;
if (genl_sock == NULL)
return 0;
switch (event) {
case CTRL_CMD_NEWFAMILY:
case CTRL_CMD_DELFAMILY:
msg = ctrl_build_msg(data, 0, 0, event);
if (IS_ERR(msg))
return PTR_ERR(msg);
genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
break;
}
return 0;
}
static struct genl_ops genl_ctrl_ops = {
.cmd = CTRL_CMD_GETFAMILY,
.doit = ctrl_getfamily,
.dumpit = ctrl_dumpfamily,
.policy = ctrl_policy,
};
static int __init genl_init(void)
{
int i, err;
for (i = 0; i < GENL_FAM_TAB_SIZE; i++)
INIT_LIST_HEAD(&family_ht[i]);
err = genl_register_family(&genl_ctrl);
if (err < 0)
goto errout;
err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops);
if (err < 0)
goto errout_register;
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID,
genl_rcv, THIS_MODULE);
if (genl_sock == NULL)
panic("GENL: Cannot initialize generic netlink\n");
return 0;
errout_register:
genl_unregister_family(&genl_ctrl);
errout:
panic("GENL: Cannot register controller: %d\n", err);
}
subsys_initcall(genl_init);
EXPORT_SYMBOL(genl_sock);
EXPORT_SYMBOL(genl_register_ops);
EXPORT_SYMBOL(genl_unregister_ops);
EXPORT_SYMBOL(genl_register_family);
EXPORT_SYMBOL(genl_unregister_family);