Release 4.11 net/netlink/genetlink.c
/*
* NETLINK Generic Netlink Family
*
* Authors: Jamal Hadi Salim
* Thomas Graf <tgraf@suug.ch>
* Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.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 <linux/bitmap.h>
#include <linux/rwsem.h>
#include <linux/idr.h>
#include <net/sock.h>
#include <net/genetlink.h>
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
static DECLARE_RWSEM(cb_lock);
atomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0);
DECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq);
void genl_lock(void)
{
mutex_lock(&genl_mutex);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 11 | 84.62% | 1 | 50.00% |
Ingo Molnar | 2 | 15.38% | 1 | 50.00% |
Total | 13 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(genl_lock);
void genl_unlock(void)
{
mutex_unlock(&genl_mutex);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 11 | 84.62% | 1 | 50.00% |
Ingo Molnar | 2 | 15.38% | 1 | 50.00% |
Total | 13 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(genl_unlock);
#ifdef CONFIG_LOCKDEP
bool lockdep_genl_is_held(void)
{
return lockdep_is_held(&genl_mutex);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 13 | 92.86% | 1 | 50.00% |
Yaowei Bai | 1 | 7.14% | 1 | 50.00% |
Total | 14 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(lockdep_genl_is_held);
#endif
static void genl_lock_all(void)
{
down_write(&cb_lock);
genl_lock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 17 | 100.00% | 1 | 100.00% |
Total | 17 | 100.00% | 1 | 100.00% |
static void genl_unlock_all(void)
{
genl_unlock();
up_write(&cb_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 17 | 100.00% | 1 | 100.00% |
Total | 17 | 100.00% | 1 | 100.00% |
static DEFINE_IDR(genl_fam_idr);
/*
* Bitmap of multicast groups that are currently in use.
*
* To avoid an allocation at boot of just one unsigned long,
* declare it global instead.
* Bit 0 is marked as already used since group 0 is invalid.
* Bit 1 is marked as already used since the drop-monitor code
* abuses the API and thinks it can statically use group 1.
* That group will typically conflict with other groups that
* any proper users use.
* Bit 16 is marked as used since it's used for generic netlink
* and the code no longer marks pre-reserved IDs as used.
* Bit 17 is marked as already used since the VFS quota code
* also abused this API and relied on family == group ID, we
* cater to that by giving it a static family and group ID.
* Bit 18 is marked as already used since the PMCRAID driver
* did the same thing as the VFS quota code (maybe copied?)
*/
static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) |
BIT(GENL_ID_VFS_DQUOT) |
BIT(GENL_ID_PMCRAID);
static unsigned long *mc_groups = &mc_group_start;
static unsigned long mc_groups_longs = 1;
static int genl_ctrl_event(int event, const struct genl_family *family,
const struct genl_multicast_group *grp,
int grp_id);
static const struct genl_family *genl_family_find_byid(unsigned int id)
{
return idr_find(&genl_fam_idr, id);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 16 | 72.73% | 1 | 50.00% |
Johannes Berg | 6 | 27.27% | 1 | 50.00% |
Total | 22 | 100.00% | 2 | 100.00% |
static const struct genl_family *genl_family_find_byname(char *name)
{
const struct genl_family *family;
unsigned int id;
idr_for_each_entry(&genl_fam_idr, family, id)
if (strcmp(family->name, name) == 0)
return family;
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 39 | 78.00% | 1 | 50.00% |
Johannes Berg | 11 | 22.00% | 1 | 50.00% |
Total | 50 | 100.00% | 2 | 100.00% |
static const struct genl_ops *genl_get_cmd(u8 cmd,
const struct genl_family *family)
{
int i;
for (i = 0; i < family->n_ops; i++)
if (family->ops[i].cmd == cmd)
return &family->ops[i];
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 31 | 50.82% | 3 | 75.00% |
Thomas Graf | 30 | 49.18% | 1 | 25.00% |
Total | 61 | 100.00% | 4 | 100.00% |
static int genl_allocate_reserve_groups(int n_groups, int *first_id)
{
unsigned long *new_groups;
int start = 0;
int i;
int id;
bool fits;
do {
if (start == 0)
id = find_first_zero_bit(mc_groups,
mc_groups_longs *
BITS_PER_LONG);
else
id = find_next_zero_bit(mc_groups,
mc_groups_longs * BITS_PER_LONG,
start);
fits = true;
for (i = id;
i < min_t(int, id + n_groups,
mc_groups_longs * BITS_PER_LONG);
i++) {
if (test_bit(i, mc_groups)) {
start = i;
fits = false;
break;
}
}
if (id + n_groups > mc_groups_longs * BITS_PER_LONG) {
unsigned long new_longs = mc_groups_longs +
BITS_TO_LONGS(n_groups);
size_t nlen = new_longs * sizeof(unsigned long);
if (mc_groups == &mc_group_start) {
new_groups = kzalloc(nlen, GFP_KERNEL);
if (!new_groups)
return -ENOMEM;
mc_groups = new_groups;
*mc_groups = mc_group_start;
} else {
new_groups = krealloc(mc_groups, nlen,
GFP_KERNEL);
if (!new_groups)
return -ENOMEM;
mc_groups = new_groups;
for (i = 0; i < BITS_TO_LONGS(n_groups); i++)
mc_groups[mc_groups_longs + i] = 0;
}
mc_groups_longs = new_longs;
}
} while (!fits);
for (i = id; i < id + n_groups; i++)
set_bit(i, mc_groups);
*first_id = id;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 253 | 91.01% | 5 | 50.00% |
Thomas Graf | 16 | 5.76% | 1 | 10.00% |
Jamal Hadi Salim | 4 | 1.44% | 1 | 10.00% |
Brian Haley | 2 | 0.72% | 1 | 10.00% |
Matti Vaittinen | 2 | 0.72% | 1 | 10.00% |
David S. Miller | 1 | 0.36% | 1 | 10.00% |
Total | 278 | 100.00% | 10 | 100.00% |
static struct genl_family genl_ctrl;
static int genl_validate_assign_mc_groups(struct genl_family *family)
{
int first_id;
int n_groups = family->n_mcgrps;
int err = 0, i;
bool groups_allocated = false;
if (!n_groups)
return 0;
for (i = 0; i < n_groups; i++) {
const struct genl_multicast_group *grp = &family->mcgrps[i];
if (WARN_ON(grp->name[0] == '\0'))
return -EINVAL;
if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL))
return -EINVAL;
}
/* special-case our own group and hacks */
if (family == &genl_ctrl) {
first_id = GENL_ID_CTRL;
BUG_ON(n_groups != 1);
} else if (strcmp(family->name, "NET_DM") == 0) {
first_id = 1;
BUG_ON(n_groups != 1);
} else if (family->id == GENL_ID_VFS_DQUOT) {
first_id = GENL_ID_VFS_DQUOT;
BUG_ON(n_groups != 1);
} else if (family->id == GENL_ID_PMCRAID) {
first_id = GENL_ID_PMCRAID;
BUG_ON(n_groups != 1);
} else {
groups_allocated = true;
err = genl_allocate_reserve_groups(n_groups, &first_id);
if (err)
return err;
}
family->mcgrp_offset = first_id;
/* if still initializing, can't and don't need to to realloc bitmaps */
if (!init_net.genl_sock)
return 0;
if (family->netnsok) {
struct net *net;
netlink_table_grab();
rcu_read_lock();
for_each_net_rcu(net) {
err = __netlink_change_ngroups(net->genl_sock,
mc_groups_longs * BITS_PER_LONG);
if (err) {
/*
* No need to roll back, can only fail if
* memory allocation fails and then the
* number of _possible_ groups has been
* increased on some sockets which is ok.
*/
break;
}
}
rcu_read_unlock();
netlink_table_ungrab();
} else {
err = netlink_change_ngroups(init_net.genl_sock,
mc_groups_longs * BITS_PER_LONG);
}
if (groups_allocated && err) {
for (i = 0; i < family->n_mcgrps; i++)
clear_bit(family->mcgrp_offset + i, mc_groups);
}
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 331 | 95.11% | 6 | 54.55% |
Thomas Graf | 11 | 3.16% | 3 | 27.27% |
Jamal Hadi Salim | 4 | 1.15% | 1 | 9.09% |
Geert Uytterhoeven | 2 | 0.57% | 1 | 9.09% |
Total | 348 | 100.00% | 11 | 100.00% |
static void genl_unregister_mc_groups(const struct genl_family *family)
{
struct net *net;
int i;
netlink_table_grab();
rcu_read_lock();
for_each_net_rcu(net) {
for (i = 0; i < family->n_mcgrps; i++)
__netlink_clear_multicast_users(
net->genl_sock, family->mcgrp_offset + i);
}
rcu_read_unlock();
netlink_table_ungrab();
for (i = 0; i < family->n_mcgrps; i++) {
int grp_id = family->mcgrp_offset + i;
if (grp_id != 1)
clear_bit(grp_id, mc_groups);
genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family,
&family->mcgrps[i], grp_id);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 111 | 90.98% | 7 | 77.78% |
Thomas Graf | 11 | 9.02% | 2 | 22.22% |
Total | 122 | 100.00% | 9 | 100.00% |
static int genl_validate_ops(const struct genl_family *family)
{
const struct genl_ops *ops = family->ops;
unsigned int n_ops = family->n_ops;
int i, j;
if (WARN_ON(n_ops && !ops))
return -EINVAL;
if (!n_ops)
return 0;
for (i = 0; i < n_ops; i++) {
if (ops[i].dumpit == NULL && ops[i].doit == NULL)
return -EINVAL;
for (j = i + 1; j < n_ops; j++)
if (ops[i].cmd == ops[j].cmd)
return -EINVAL;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 133 | 99.25% | 5 | 83.33% |
Cheng Renquan | 1 | 0.75% | 1 | 16.67% |
Total | 134 | 100.00% | 6 | 100.00% |
/**
* 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's ops, multicast groups and module pointer must already
* be assigned.
*
* Return 0 on success or a negative error code.
*/
int genl_register_family(struct genl_family *family)
{
int err, i;
int start = GENL_START_ALLOC, end = GENL_MAX_ID;
err = genl_validate_ops(family);
if (err)
return err;
genl_lock_all();
if (genl_family_find_byname(family->name)) {
err = -EEXIST;
goto errout_locked;
}
/*
* Sadly, a few cases need to be special-cased
* due to them having previously abused the API
* and having used their family ID also as their
* multicast group ID, so we use reserved IDs
* for both to be sure we can do that mapping.
*/
if (family == &genl_ctrl) {
/* and this needs to be special for initial family lookups */
start = end = GENL_ID_CTRL;
} else if (strcmp(family->name, "pmcraid") == 0) {
start = end = GENL_ID_PMCRAID;
} else if (strcmp(family->name, "VFS_DQUOT") == 0) {
start = end = GENL_ID_VFS_DQUOT;
}
if (family->maxattr && !family->parallel_ops) {
family->attrbuf = kmalloc((family->maxattr+1) *
sizeof(struct nlattr *), GFP_KERNEL);
if (family->attrbuf == NULL) {
err = -ENOMEM;
goto errout_locked;
}
} else
family->attrbuf = NULL;
family->id = idr_alloc(&genl_fam_idr, family,
start, end + 1, GFP_KERNEL);
if (family->id < 0) {
err = family->id;
goto errout_locked;
}
err = genl_validate_assign_mc_groups(family);
if (err)
goto errout_remove;
genl_unlock_all();
/* send all events */
genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0);
for (i = 0; i < family->n_mcgrps; i++)
genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family,
&family->mcgrps[i], family->mcgrp_offset + i);
return 0;
errout_remove:
idr_remove(&genl_fam_idr, family->id);
kfree(family->attrbuf);
errout_locked:
genl_unlock_all();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 159 | 50.16% | 6 | 50.00% |
Thomas Graf | 123 | 38.80% | 1 | 8.33% |
Wei Yongjun | 10 | 3.15% | 1 | 8.33% |
Krishna Kumar | 9 | 2.84% | 1 | 8.33% |
Pravin B Shelar | 8 | 2.52% | 1 | 8.33% |
Américo Wang | 7 | 2.21% | 1 | 8.33% |
Jamal Hadi Salim | 1 | 0.32% | 1 | 8.33% |
Total | 317 | 100.00% | 12 | 100.00% |
EXPORT_SYMBOL(genl_register_family);
/**
* 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(const struct genl_family *family)
{
genl_lock_all();
if (!genl_family_find_byid(family->id)) {
genl_unlock_all();
return -ENOENT;
}
genl_unregister_mc_groups(family);
idr_remove(&genl_fam_idr, family->id);
up_write(&cb_lock);
wait_event(genl_sk_destructing_waitq,
atomic_read(&genl_sk_destructing_cnt) == 0);
genl_unlock();
kfree(family->attrbuf);
genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 46 | 50.55% | 4 | 50.00% |
Thomas Graf | 41 | 45.05% | 1 | 12.50% |
Pavel Emelyanov | 2 | 2.20% | 1 | 12.50% |
Pravin B Shelar | 1 | 1.10% | 1 | 12.50% |
pravin shelar | 1 | 1.10% | 1 | 12.50% |
Total | 91 | 100.00% | 8 | 100.00% |
EXPORT_SYMBOL(genl_unregister_family);
/**
* genlmsg_put - Add generic netlink header to netlink message
* @skb: socket buffer holding the message
* @portid: netlink portid the message is addressed to
* @seq: sequence number (usually the one of the sender)
* @family: generic netlink family
* @flags: netlink message flags
* @cmd: generic netlink command
*
* Returns pointer to user specific header
*/
void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
const struct genl_family *family, int flags, u8 cmd)
{
struct nlmsghdr *nlh;
struct genlmsghdr *hdr;
nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN +
family->hdrsize, flags);
if (nlh == NULL)
return NULL;
hdr = nlmsg_data(nlh);
hdr->cmd = cmd;
hdr->version = family->version;
hdr->reserved = 0;
return (char *) hdr + GENL_HDRLEN;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Denys Vlasenko | 104 | 97.20% | 1 | 33.33% |
Eric W. Biedermann | 2 | 1.87% | 1 | 33.33% |
Johannes Berg | 1 | 0.93% | 1 | 33.33% |
Total | 107 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL(genlmsg_put);
static int genl_lock_start(struct netlink_callback *cb)
{
/* our ops are always const - netlink API doesn't propagate that */
const struct genl_ops *ops = cb->data;
int rc = 0;
if (ops->start) {
genl_lock();
rc = ops->start(cb);
genl_unlock();
}
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tom Herbert | 53 | 100.00% | 1 | 100.00% |
Total | 53 | 100.00% | 1 | 100.00% |
static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
/* our ops are always const - netlink API doesn't propagate that */
const struct genl_ops *ops = cb->data;
int rc;
genl_lock();
rc = ops->dumpit(skb, cb);
genl_unlock();
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 48 | 96.00% | 1 | 50.00% |
Johannes Berg | 2 | 4.00% | 1 | 50.00% |
Total | 50 | 100.00% | 2 | 100.00% |
static int genl_lock_done(struct netlink_callback *cb)
{
/* our ops are always const - netlink API doesn't propagate that */
const struct genl_ops *ops = cb->data;
int rc = 0;
if (ops->done) {
genl_lock();
rc = ops->done(cb);
genl_unlock();
}
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 51 | 96.23% | 1 | 50.00% |
Johannes Berg | 2 | 3.77% | 1 | 50.00% |
Total | 53 | 100.00% | 2 | 100.00% |
static int genl_family_rcv_msg(const struct genl_family *family,
struct sk_buff *skb,
struct nlmsghdr *nlh)
{
const struct genl_ops *ops;
struct net *net = sock_net(skb->sk);
struct genl_info info;
struct genlmsghdr *hdr = nlmsg_data(nlh);
struct nlattr **attrbuf;
int hdrlen, err;
/* this family doesn't exist in this netns */
if (!family->netnsok && !net_eq(net, &init_net))
return -ENOENT;
hdrlen = GENL_HDRLEN + family->hdrsize;
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
return -EINVAL;
ops = genl_get_cmd(hdr->cmd, family);
if (ops == NULL)
return -EOPNOTSUPP;
if ((ops->flags & GENL_ADMIN_PERM) &&
!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;
if ((ops->flags & GENL_UNS_ADMIN_PERM) &&
!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) {
int rc;
if (ops->dumpit == NULL)
return -EOPNOTSUPP;
if (!family->parallel_ops) {
struct netlink_dump_control c = {
.module = family->module,
/* we have const, but the netlink API doesn't */
.data = (void *)ops,
.start = genl_lock_start,
.dump = genl_lock_dumpit,
.done = genl_lock_done,
};
genl_unlock();
rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
genl_lock();
} else {
struct netlink_dump_control c = {
.module = family->module,
.start = ops->start,
.dump = ops->dumpit,
.done = ops->done,
};
rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
}
return rc;
}
if (ops->doit == NULL)
return -EOPNOTSUPP;
if (family->maxattr && family->parallel_ops) {
attrbuf = kmalloc((family->maxattr+1) *
sizeof(struct nlattr *), GFP_KERNEL);
if (attrbuf == NULL)
return -ENOMEM;
} else
attrbuf = family->attrbuf;
if (attrbuf) {
err = nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr,
ops->policy);
if (err < 0)
goto out;
}
info.snd_seq = nlh->nlmsg_seq;
info.snd_portid = NETLINK_CB(skb).portid;
info.nlhdr = nlh;
info.genlhdr = nlmsg_data(nlh);
info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
info.attrs = attrbuf;
genl_info_net_set(&info, net);
memset(&info.user_ptr, 0, sizeof(info.user_ptr));
if (family->pre_doit) {
err = family->pre_doit(ops, skb, &info);
if (err)
goto out;
}
err = ops->doit(skb, &info);
if (family->post_doit)
family->post_doit(ops, skb, &info);
out:
if (family->parallel_ops)
kfree(attrbuf);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 221 | 38.10% | 2 | 11.11% |
Pravin B Shelar | 167 | 28.79% | 3 | 16.67% |
Johannes Berg | 114 | 19.66% | 4 | 22.22% |
Tycho Andersen | 26 | 4.48% | 1 | 5.56% |
Pablo Neira Ayuso | 25 | 4.31% | 2 | 11.11% |
Tom Herbert | 12 | 2.07% | 1 | 5.56% |
Wei Yongjun | 8 | 1.38% | 1 | 5.56% |
Eric W. Biedermann | 5 | 0.86% | 2 | 11.11% |
Darrel Goeddel | 1 | 0.17% | 1 | 5.56% |
Eric Paris | 1 | 0.17% | 1 | 5.56% |
Total | 580 | 100.00% | 18 | 100.00% |
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
const struct genl_family *family;
int err;
family = genl_family_find_byid(nlh->nlmsg_type);
if (family == NULL)
return -ENOENT;
if (!family->parallel_ops)
genl_lock();
err = genl_family_rcv_msg(family, skb, nlh);
if (!family->parallel_ops)
genl_unlock();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 73 | 93.59% | 1 | 25.00% |
Johannes Berg | 3 | 3.85% | 2 | 50.00% |
Thomas Graf | 2 | 2.56% | 1 | 25.00% |
Total | 78 | 100.00% | 4 | 100.00% |
static void genl_rcv(struct sk_buff *skb)
{
down_read(&cb_lock);
netlink_rcv_skb(skb, &genl_rcv_msg);
up_read(&cb_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 15 | 48.39% | 1 | 25.00% |
Pravin B Shelar | 10 | 32.26% | 1 | 25.00% |
Denis V. Lunev | 6 | 19.35% | 2 | 50.00% |
Total | 31 | 100.00% | 4 | 100.00% |
/**************************************************************************
* Controller
**************************************************************************/
static struct genl_family genl_ctrl;
static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq,
u32 flags, struct sk_buff *skb, u8 cmd)
{
void *hdr;
hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd);
if (hdr == NULL)
return -1;
if (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))
goto nla_put_failure;
if (family->n_ops) {
struct nlattr *nla_ops;
int i;
nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
if (nla_ops == NULL)
goto nla_put_failure;
for (i = 0; i < family->n_ops; i++) {
struct nlattr *nest;
const struct genl_ops *ops = &family->ops[i];
u32 op_flags = ops->flags;
if (ops->dumpit)
op_flags |= GENL_CMD_CAP_DUMP;
if (ops->doit)
op_flags |= GENL_CMD_CAP_DO;
if (ops->policy)
op_flags |= GENL_CMD_CAP_HASPOL;
nest = nla_nest_start(skb, i + 1);
if (nest == NULL)
goto nla_put_failure;
if (nla_put_u32(skb, CTRL_ATTR_OP_ID, ops->cmd) ||
nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags))
goto nla_put_failure;
nla_nest_end(skb, nest);
}
nla_nest_end(skb, nla_ops);
}
if (family->n_mcgrps) {
struct nlattr *nla_grps;
int i;
nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
if (nla_grps == NULL)
goto nla_put_failure;
for (i = 0; i < family->n_mcgrps; i++) {
struct nlattr *nest;
const struct genl_multicast_group *grp;
grp = &family->mcgrps[i];
nest = nla_nest_start(skb, i + 1);
if (nest == NULL)
goto nla_put_failure;
if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID,
family->mcgrp_offset + i) ||
nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,
grp->name))
goto nla_put_failure;
nla_nest_end(skb, nest);
}
nla_nest_end(skb, nla_grps);
}
genlmsg_end(skb, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(skb, hdr);
return -EMSGSIZE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 335 | 75.28% | 7 | 58.33% |
Thomas Graf | 75 | 16.85% | 3 | 25.00% |
David S. Miller | 33 | 7.42% | 1 | 8.33% |
Eric W. Biedermann | 2 | 0.45% | 1 | 8.33% |
Total | 445 | 100.00% | 12 | 100.00% |
static int ctrl_fill_mcgrp_info(const struct genl_family *family,
const struct genl_multicast_group *grp,
int grp_id, u32 portid, u32 seq, u32 flags,
struct sk_buff *skb, u8 cmd)
{
void *hdr;
struct nlattr *nla_grps;
struct nlattr *nest;
hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd);
if (hdr == NULL)
return -1;
if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) ||
nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id))
goto nla_put_failure;
nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
if (nla_grps == NULL)
goto nla_put_failure;
nest = nla_nest_start(skb, 1);
if (nest == NULL)
goto nla_put_failure;
if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) ||
nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME,
grp->name))
goto nla_put_failure;
nla_nest_end(skb, nest);
nla_nest_end(skb, nla_grps);
genlmsg_end(skb, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(skb, hdr);
return -EMSGSIZE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 105 | 51.22% | 5 | 50.00% |
Thomas Graf | 80 | 39.02% | 3 | 30.00% |
David S. Miller | 18 | 8.78% | 1 | 10.00% |
Eric W. Biedermann | 2 | 0.98% | 1 | 10.00% |
Total | 205 | 100.00% | 10 | 100.00% |
static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
{
int n = 0;
struct genl_family *rt;
struct net *net = sock_net(skb->sk);
int fams_to_skip = cb->args[0];
unsigned int id;
idr_for_each_entry(&genl_fam_idr, rt, id) {
if (!rt->netnsok && !net_eq(net, &init_net))
continue;
if (n++ < fams_to_skip)
continue;
if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
skb, CTRL_CMD_NEWFAMILY) < 0) {
n--;
break;
}
}
cb->args[0] = n;
return skb->len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 93 | 67.88% | 1 | 20.00% |
Johannes Berg | 38 | 27.74% | 2 | 40.00% |
Stanislaw Gruszka | 5 | 3.65% | 1 | 20.00% |
Eric W. Biedermann | 1 | 0.73% | 1 | 20.00% |
Total | 137 | 100.00% | 5 | 100.00% |
static struct sk_buff *ctrl_build_family_msg(const struct genl_family *family,
u32 portid, 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, portid, seq, 0, skb, cmd);
if (err < 0) {
nlmsg_free(skb);
return ERR_PTR(err);
}
return skb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 87 | 94.57% | 3 | 42.86% |
Eric W. Biedermann | 2 | 2.17% | 1 | 14.29% |
Johannes Berg | 2 | 2.17% | 2 | 28.57% |
Per Liden | 1 | 1.09% | 1 | 14.29% |
Total | 92 | 100.00% | 7 | 100.00% |
static struct sk_buff *
ctrl_build_mcgrp_msg(const struct genl_family *family,
const struct genl_multicast_group *grp,
int grp_id, u32 portid, 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_mcgrp_info(family, grp, grp_id, portid,
seq, 0, skb, cmd);
if (err < 0) {
nlmsg_free(skb);
return ERR_PTR(err);
}
return skb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 103 | 98.10% | 4 | 80.00% |
Eric W. Biedermann | 2 | 1.90% | 1 | 20.00% |
Total | 105 | 100.00% | 5 | 100.00% |
static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[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;
const 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);
err = -ENOENT;
}
if (info->attrs[CTRL_ATTR_FAMILY_NAME]) {
char *name;
name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
res = genl_family_find_byname(name);
#ifdef CONFIG_MODULES
if (res == NULL) {
genl_unlock();
up_read(&cb_lock);
request_module("net-pf-%d-proto-%d-family-%s",
PF_NETLINK, NETLINK_GENERIC, name);
down_read(&cb_lock);
genl_lock();
res = genl_family_find_byname(name);
}
#endif
err = -ENOENT;
}
if (res == NULL)
return err;
if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) {
/* family doesn't exist here */
return -ENOENT;
}
msg = ctrl_build_family_msg(res, info->snd_portid, info->snd_seq,
CTRL_CMD_NEWFAMILY);
if (IS_ERR(msg))
return PTR_ERR(msg);
return genlmsg_reply(msg, info);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 142 | 61.21% | 3 | 30.00% |
Johannes Berg | 40 | 17.24% | 3 | 30.00% |
Stephen Hemminger | 36 | 15.52% | 1 | 10.00% |
Stanislaw Gruszka | 12 | 5.17% | 1 | 10.00% |
Eric W. Biedermann | 1 | 0.43% | 1 | 10.00% |
Neil Horman | 1 | 0.43% | 1 | 10.00% |
Total | 232 | 100.00% | 10 | 100.00% |
static int genl_ctrl_event(int event, const struct genl_family *family,
const struct genl_multicast_group *grp,
int grp_id)
{
struct sk_buff *msg;
/* genl is still initialising */
if (!init_net.genl_sock)
return 0;
switch (event) {
case CTRL_CMD_NEWFAMILY:
case CTRL_CMD_DELFAMILY:
WARN_ON(grp);
msg = ctrl_build_family_msg(family, 0, 0, event);
break;
case CTRL_CMD_NEWMCAST_GRP:
case CTRL_CMD_DELMCAST_GRP:
BUG_ON(!grp);
msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event);
break;
default:
return -EINVAL;
}
if (IS_ERR(msg))
return PTR_ERR(msg);
if (!family->netnsok) {
genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0,
0, GFP_KERNEL);
} else {
rcu_read_lock();
genlmsg_multicast_allns(&genl_ctrl, msg, 0,
0, GFP_ATOMIC);
rcu_read_unlock();
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 110 | 64.33% | 6 | 75.00% |
Thomas Graf | 61 | 35.67% | 2 | 25.00% |
Total | 171 | 100.00% | 8 | 100.00% |
static const struct genl_ops genl_ctrl_ops[] = {
{
.cmd = CTRL_CMD_GETFAMILY,
.doit = ctrl_getfamily,
.dumpit = ctrl_dumpfamily,
.policy = ctrl_policy,
},
};
static const struct genl_multicast_group genl_ctrl_groups[] = {
{ .name = "notify", },
};
static struct genl_family genl_ctrl __ro_after_init = {
.module = THIS_MODULE,
.ops = genl_ctrl_ops,
.n_ops = ARRAY_SIZE(genl_ctrl_ops),
.mcgrps = genl_ctrl_groups,
.n_mcgrps = ARRAY_SIZE(genl_ctrl_groups),
.id = GENL_ID_CTRL,
.name = "nlctrl",
.version = 0x2,
.maxattr = CTRL_ATTR_MAX,
.netnsok = true,
};
static int genl_bind(struct net *net, int group)
{
struct genl_family *f;
int err = -ENOENT;
unsigned int id;
down_read(&cb_lock);
idr_for_each_entry(&genl_fam_idr, f, id) {
if (group >= f->mcgrp_offset &&
group < f->mcgrp_offset + f->n_mcgrps) {
int fam_grp = group - f->mcgrp_offset;
if (!f->netnsok && net != &init_net)
err = -ENOENT;
else if (f->mcast_bind)
err = f->mcast_bind(net, fam_grp);
else
err = 0;
break;
}
}
up_read(&cb_lock);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 124 | 100.00% | 4 | 100.00% |
Total | 124 | 100.00% | 4 | 100.00% |
static void genl_unbind(struct net *net, int group)
{
struct genl_family *f;
unsigned int id;
down_read(&cb_lock);
idr_for_each_entry(&genl_fam_idr, f, id) {
if (group >= f->mcgrp_offset &&
group < f->mcgrp_offset + f->n_mcgrps) {
int fam_grp = group - f->mcgrp_offset;
if (f->mcast_unbind)
f->mcast_unbind(net, fam_grp);
break;
}
}
up_read(&cb_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 90 | 100.00% | 3 | 100.00% |
Total | 90 | 100.00% | 3 | 100.00% |
static int __net_init genl_pernet_init(struct net *net)
{
struct netlink_kernel_cfg cfg = {
.input = genl_rcv,
.flags = NL_CFG_F_NONROOT_RECV,
.bind = genl_bind,
.unbind = genl_unbind,
};
/* we'll bump the group number right afterwards */
net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg);
if (!net->genl_sock && net_eq(net, &init_net))
panic("GENL: Cannot initialize generic netlink\n");
if (!net->genl_sock)
return -ENOMEM;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 70 | 80.46% | 2 | 50.00% |
Pablo Neira Ayuso | 17 | 19.54% | 2 | 50.00% |
Total | 87 | 100.00% | 4 | 100.00% |
static void __net_exit genl_pernet_exit(struct net *net)
{
netlink_kernel_release(net->genl_sock);
net->genl_sock = NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 25 | 100.00% | 1 | 100.00% |
Total | 25 | 100.00% | 1 | 100.00% |
static struct pernet_operations genl_pernet_ops = {
.init = genl_pernet_init,
.exit = genl_pernet_exit,
};
static int __init genl_init(void)
{
int err;
err = genl_register_family(&genl_ctrl);
if (err < 0)
goto problem;
err = register_pernet_subsys(&genl_pernet_ops);
if (err)
goto problem;
return 0;
problem:
panic("GENL: Cannot register controller: %d\n", err);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Graf | 45 | 80.36% | 1 | 25.00% |
Johannes Berg | 10 | 17.86% | 2 | 50.00% |
Eric W. Biedermann | 1 | 1.79% | 1 | 25.00% |
Total | 56 | 100.00% | 4 | 100.00% |
subsys_initcall(genl_init);
/**
* genl_family_attrbuf - return family's attrbuf
* @family: the family
*
* Return the family's attrbuf, while validating that it's
* actually valid to access it.
*
* You cannot use this function with a family that has parallel_ops
* and you can only use it within (pre/post) doit/dumpit callbacks.
*/
struct nlattr **genl_family_attrbuf(const struct genl_family *family)
{
if (!WARN_ON(family->parallel_ops))
lockdep_assert_held(&genl_mutex);
return family->attrbuf;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 35 | 100.00% | 2 | 100.00% |
Total | 35 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL(genl_family_attrbuf);
static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
gfp_t flags)
{
struct sk_buff *tmp;
struct net *net, *prev = NULL;
int err;
for_each_net_rcu(net) {
if (prev) {
tmp = skb_clone(skb, flags);
if (!tmp) {
err = -ENOMEM;
goto error;
}
err = nlmsg_multicast(prev->genl_sock, tmp,
portid, group, flags);
if (err)
goto error;
}
prev = net;
}
return nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
error:
kfree_skb(skb);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 126 | 97.67% | 1 | 50.00% |
Eric W. Biedermann | 3 | 2.33% | 1 | 50.00% |
Total | 129 | 100.00% | 2 | 100.00% |
int genlmsg_multicast_allns(const struct genl_family *family,
struct sk_buff *skb, u32 portid,
unsigned int group, gfp_t flags)
{
if (WARN_ON_ONCE(group >= family->n_mcgrps))
return -EINVAL;
group = family->mcgrp_offset + group;
return genlmsg_mcast(skb, portid, group, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 59 | 96.72% | 5 | 83.33% |
Eric W. Biedermann | 2 | 3.28% | 1 | 16.67% |
Total | 61 | 100.00% | 6 | 100.00% |
EXPORT_SYMBOL(genlmsg_multicast_allns);
void genl_notify(const struct genl_family *family, struct sk_buff *skb,
struct genl_info *info, u32 group, gfp_t flags)
{
struct net *net = genl_info_net(info);
struct sock *sk = net->genl_sock;
int report = 0;
if (info->nlhdr)
report = nlmsg_report(info->nlhdr);
if (WARN_ON_ONCE(group >= family->n_mcgrps))
return;
group = family->mcgrp_offset + group;
nlmsg_notify(sk, skb, info->snd_portid, group, report, flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Pravin B Shelar | 56 | 54.37% | 1 | 16.67% |
Johannes Berg | 26 | 25.24% | 4 | 66.67% |
Jiri Benc | 21 | 20.39% | 1 | 16.67% |
Total | 103 | 100.00% | 6 | 100.00% |
EXPORT_SYMBOL(genl_notify);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 2695 | 54.74% | 28 | 31.82% |
Thomas Graf | 1253 | 25.45% | 13 | 14.77% |
Pravin B Shelar | 484 | 9.83% | 5 | 5.68% |
Denys Vlasenko | 109 | 2.21% | 1 | 1.14% |
Tom Herbert | 65 | 1.32% | 1 | 1.14% |
David S. Miller | 52 | 1.06% | 2 | 2.27% |
Pablo Neira Ayuso | 42 | 0.85% | 4 | 4.55% |
Stephen Hemminger | 38 | 0.77% | 2 | 2.27% |
Tycho Andersen | 26 | 0.53% | 1 | 1.14% |
Eric W. Biedermann | 24 | 0.49% | 3 | 3.41% |
Jiri Benc | 21 | 0.43% | 1 | 1.14% |
Wei Yongjun | 18 | 0.37% | 2 | 2.27% |
Stanislaw Gruszka | 17 | 0.35% | 2 | 2.27% |
James Chapman | 10 | 0.20% | 1 | 1.14% |
Krishna Kumar | 9 | 0.18% | 1 | 1.14% |
Jamal Hadi Salim | 9 | 0.18% | 2 | 2.27% |
Ingo Molnar | 9 | 0.18% | 1 | 1.14% |
Changli Gao | 9 | 0.18% | 1 | 1.14% |
Américo Wang | 8 | 0.16% | 2 | 2.27% |
Denis V. Lunev | 6 | 0.12% | 2 | 2.27% |
Tejun Heo | 3 | 0.06% | 1 | 1.14% |
Brian Haley | 2 | 0.04% | 1 | 1.14% |
Geert Uytterhoeven | 2 | 0.04% | 1 | 1.14% |
Matti Vaittinen | 2 | 0.04% | 1 | 1.14% |
Pavel Emelyanov | 2 | 0.04% | 1 | 1.14% |
Darrel Goeddel | 1 | 0.02% | 1 | 1.14% |
Yaowei Bai | 1 | 0.02% | 1 | 1.14% |
Eric Paris | 1 | 0.02% | 1 | 1.14% |
Cheng Renquan | 1 | 0.02% | 1 | 1.14% |
Neil Horman | 1 | 0.02% | 1 | 1.14% |
Per Liden | 1 | 0.02% | 1 | 1.14% |
Patrick McHardy | 1 | 0.02% | 1 | 1.14% |
pravin shelar | 1 | 0.02% | 1 | 1.14% |
Total | 4923 | 100.00% | 88 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.