Skip to content

Commit

Permalink
genetlink: make multicast groups const, prevent abuse
Browse files Browse the repository at this point in the history
Register generic netlink multicast groups as an array with
the family and give them contiguous group IDs. Then instead
of passing the global group ID to the various functions that
send messages, pass the ID relative to the family - for most
families that's just 0 because the only have one group.

This avoids the list_head and ID in each group, adding a new
field for the mcast group ID offset to the family.

At the same time, this allows us to prevent abusing groups
again like the quota and dropmon code did, since we can now
check that a family only uses a group it owns.

Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
jmberg-intel authored and davem330 committed Nov 19, 2013
1 parent 68eb550 commit 2a94fe4
Show file tree
Hide file tree
Showing 19 changed files with 377 additions and 433 deletions.
26 changes: 8 additions & 18 deletions drivers/acpi/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,17 @@ enum {
#define ACPI_GENL_VERSION 0x01
#define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group"

static const struct genl_multicast_group acpi_event_mcgrps[] = {
{ .name = ACPI_GENL_MCAST_GROUP_NAME, },
};

static struct genl_family acpi_event_genl_family = {
.id = GENL_ID_GENERATE,
.name = ACPI_GENL_FAMILY_NAME,
.version = ACPI_GENL_VERSION,
.maxattr = ACPI_GENL_ATTR_MAX,
};

static struct genl_multicast_group acpi_event_mcgrp = {
.name = ACPI_GENL_MCAST_GROUP_NAME,
.mcgrps = acpi_event_mcgrps,
.n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps),
};

int acpi_bus_generate_netlink_event(const char *device_class,
Expand Down Expand Up @@ -146,27 +148,15 @@ int acpi_bus_generate_netlink_event(const char *device_class,
return result;
}

genlmsg_multicast(&acpi_event_genl_family,
skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC);
return 0;
}

EXPORT_SYMBOL(acpi_bus_generate_netlink_event);

static int acpi_event_genetlink_init(void)
{
int result;

result = genl_register_family(&acpi_event_genl_family);
if (result)
return result;

result = genl_register_mc_group(&acpi_event_genl_family,
&acpi_event_mcgrp);
if (result)
genl_unregister_family(&acpi_event_genl_family);

return result;
return genl_register_family(&acpi_event_genl_family);
}

#else
Expand Down
25 changes: 5 additions & 20 deletions drivers/net/team/team.c
Original file line number Diff line number Diff line change
Expand Up @@ -2670,16 +2670,15 @@ static const struct genl_ops team_nl_ops[] = {
},
};

static struct genl_multicast_group team_change_event_mcgrp = {
.name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
static const struct genl_multicast_group team_nl_mcgrps[] = {
{ .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
};

static int team_nl_send_multicast(struct sk_buff *skb,
struct team *team, u32 portid)
{
return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
skb, 0, team_change_event_mcgrp.id,
GFP_KERNEL);
skb, 0, 0, GFP_KERNEL);
}

static int team_nl_send_event_options_get(struct team *team,
Expand All @@ -2698,22 +2697,8 @@ static int team_nl_send_event_port_get(struct team *team,

static int team_nl_init(void)
{
int err;

err = genl_register_family_with_ops(&team_nl_family, team_nl_ops);
if (err)
return err;

err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
if (err)
goto err_change_event_grp_reg;

return 0;

err_change_event_grp_reg:
genl_unregister_family(&team_nl_family);

return err;
return genl_register_family_with_ops_groups(&team_nl_family, team_nl_ops,
team_nl_mcgrps);
}

static void team_nl_fini(void)
Expand Down
24 changes: 8 additions & 16 deletions drivers/thermal/thermal_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1606,15 +1606,17 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);

#ifdef CONFIG_NET
static const struct genl_multicast_group thermal_event_mcgrps[] = {
{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
};

static struct genl_family thermal_event_genl_family = {
.id = GENL_ID_GENERATE,
.name = THERMAL_GENL_FAMILY_NAME,
.version = THERMAL_GENL_VERSION,
.maxattr = THERMAL_GENL_ATTR_MAX,
};

static struct genl_multicast_group thermal_event_mcgrp = {
.name = THERMAL_GENL_MCAST_GROUP_NAME,
.mcgrps = thermal_event_mcgrps,
.n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),
};

int thermal_generate_netlink_event(struct thermal_zone_device *tz,
Expand Down Expand Up @@ -1676,7 +1678,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz,
}

result = genlmsg_multicast(&thermal_event_genl_family, skb, 0,
thermal_event_mcgrp.id, GFP_ATOMIC);
0, GFP_ATOMIC);
if (result)
dev_err(&tz->device, "Failed to send netlink event:%d", result);

Expand All @@ -1686,17 +1688,7 @@ EXPORT_SYMBOL_GPL(thermal_generate_netlink_event);

static int genetlink_init(void)
{
int result;

result = genl_register_family(&thermal_event_genl_family);
if (result)
return result;

result = genl_register_mc_group(&thermal_event_genl_family,
&thermal_event_mcgrp);
if (result)
genl_unregister_family(&thermal_event_genl_family);
return result;
return genl_register_family(&thermal_event_genl_family);
}

static void genetlink_exit(void)
Expand Down
15 changes: 7 additions & 8 deletions fs/quota/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <net/netlink.h>
#include <net/genetlink.h>

static const struct genl_multicast_group quota_mcgrps[] = {
{ .name = "events", },
};

/* Netlink family structure for quota */
static struct genl_family quota_genl_family = {
/*
Expand All @@ -22,10 +26,8 @@ static struct genl_family quota_genl_family = {
.name = "VFS_DQUOT",
.version = 1,
.maxattr = QUOTA_NL_A_MAX,
};

static struct genl_multicast_group quota_mcgrp = {
.name = "events",
.mcgrps = quota_mcgrps,
.n_mcgrps = ARRAY_SIZE(quota_mcgrps),
};

/**
Expand Down Expand Up @@ -88,7 +90,7 @@ void quota_send_warning(struct kqid qid, dev_t dev,
goto attr_err_out;
genlmsg_end(skb, msg_head);

genlmsg_multicast(&quota_genl_family, skb, 0, quota_mcgrp.id, GFP_NOFS);
genlmsg_multicast(&quota_genl_family, skb, 0, 0, GFP_NOFS);
return;
attr_err_out:
printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
Expand All @@ -102,9 +104,6 @@ static int __init quota_init(void)
if (genl_register_family(&quota_genl_family) != 0)
printk(KERN_ERR
"VFS: Failed to create quota netlink interface.\n");
if (genl_register_mc_group(&quota_genl_family, &quota_mcgrp))
printk(KERN_ERR
"VFS: Failed to register quota mcast group.\n");
return 0;
};

Expand Down
49 changes: 20 additions & 29 deletions include/linux/genl_magic_func.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,49 +273,40 @@ static struct genl_family ZZZ_genl_family __read_mostly = {
* Magic: define multicast groups
* Magic: define multicast group registration helper
*/
#define ZZZ_genl_mcgrps CONCAT_(GENL_MAGIC_FAMILY, _genl_mcgrps)
static const struct genl_multicast_group ZZZ_genl_mcgrps[] = {
#undef GENL_mc_group
#define GENL_mc_group(group) { .name = #group, },
#include GENL_MAGIC_INCLUDE_FILE
};

enum CONCAT_(GENL_MAGIC_FAMILY, group_ids) {
#undef GENL_mc_group
#define GENL_mc_group(group) CONCAT_(GENL_MAGIC_FAMILY, _group_ ## group),
#include GENL_MAGIC_INCLUDE_FILE
};

#undef GENL_mc_group
#define GENL_mc_group(group) \
static struct genl_multicast_group \
CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group) __read_mostly = { \
.name = #group, \
}; \
static int CONCAT_(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)( \
struct sk_buff *skb, gfp_t flags) \
{ \
unsigned int group_id = \
CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id; \
if (!group_id) \
return -EINVAL; \
CONCAT_(GENL_MAGIC_FAMILY, _group_ ## group); \
return genlmsg_multicast(&ZZZ_genl_family, skb, 0, \
group_id, flags); \
}

#include GENL_MAGIC_INCLUDE_FILE

int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void)
{
int err = genl_register_family_with_ops(&ZZZ_genl_family, ZZZ_genl_ops);
if (err)
return err;
#undef GENL_mc_group
#define GENL_mc_group(group) \
err = genl_register_mc_group(&ZZZ_genl_family, \
&CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group)); \
if (err) \
goto fail; \
else \
pr_info("%s: mcg %s: %u\n", #group, \
__stringify(GENL_MAGIC_FAMILY), \
CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id);

#include GENL_MAGIC_INCLUDE_FILE

#undef GENL_mc_group
#define GENL_mc_group(group)
return 0;
fail:
genl_unregister_family(&ZZZ_genl_family);
return err;

int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void)
{
return genl_register_family_with_ops_groups(&ZZZ_genl_family, \
ZZZ_genl_ops, \
ZZZ_genl_mcgrps);
}

void CONCAT_(GENL_MAGIC_FAMILY, _genl_unregister)(void)
Expand Down
48 changes: 31 additions & 17 deletions include/net/genetlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@
/**
* struct genl_multicast_group - generic netlink multicast group
* @name: name of the multicast group, names are per-family
* @id: multicast group ID, assigned by the core, to use with
* genlmsg_multicast().
* @list: list entry for linking
*/
struct genl_multicast_group {
struct list_head list; /* private */
char name[GENL_NAMSIZ];
u32 id;
};

struct genl_ops;
Expand All @@ -38,7 +33,9 @@ struct genl_info;
* undo operations done by pre_doit, for example release locks
* @attrbuf: buffer to store parsed attributes
* @family_list: family list
* @mcast_groups: multicast groups list
* @mcgrps: multicast groups used by this family (private)
* @n_mcgrps: number of multicast groups (private)
* @mcgrp_offset: starting number of multicast group IDs in this family
* @ops: the operations supported by this family (private)
* @n_ops: number of operations supported by this family (private)
*/
Expand All @@ -58,9 +55,11 @@ struct genl_family {
struct genl_info *info);
struct nlattr ** attrbuf; /* private */
const struct genl_ops * ops; /* private */
const struct genl_multicast_group *mcgrps; /* private */
unsigned int n_ops; /* private */
unsigned int n_mcgrps; /* private */
unsigned int mcgrp_offset; /* private */
struct list_head family_list; /* private */
struct list_head mcast_groups; /* private */
struct module *module;
};

Expand Down Expand Up @@ -150,22 +149,30 @@ static inline int genl_register_family(struct genl_family *family)
*
* Return 0 on success or a negative error code.
*/
static inline int _genl_register_family_with_ops(struct genl_family *family,
const struct genl_ops *ops,
size_t n_ops)
static inline int
_genl_register_family_with_ops_grps(struct genl_family *family,
const struct genl_ops *ops, size_t n_ops,
const struct genl_multicast_group *mcgrps,
size_t n_mcgrps)
{
family->module = THIS_MODULE;
family->ops = ops;
family->n_ops = n_ops;
family->mcgrps = mcgrps;
family->n_mcgrps = n_mcgrps;
return __genl_register_family(family);
}

#define genl_register_family_with_ops(family, ops) \
_genl_register_family_with_ops((family), (ops), ARRAY_SIZE(ops))
#define genl_register_family_with_ops(family, ops) \
_genl_register_family_with_ops_grps((family), \
(ops), ARRAY_SIZE(ops), \
NULL, 0)
#define genl_register_family_with_ops_groups(family, ops, grps) \
_genl_register_family_with_ops_grps((family), \
(ops), ARRAY_SIZE(ops), \
(grps), ARRAY_SIZE(grps))

int genl_unregister_family(struct genl_family *family);
int genl_register_mc_group(struct genl_family *family,
struct genl_multicast_group *grp);
void genl_notify(struct genl_family *family,
struct sk_buff *skb, struct net *net, u32 portid,
u32 group, struct nlmsghdr *nlh, gfp_t flags);
Expand Down Expand Up @@ -251,13 +258,16 @@ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr)
* @net: the net namespace
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: multicast group id
* @group: offset of multicast group in groups array
* @flags: allocation flags
*/
static inline int genlmsg_multicast_netns(struct genl_family *family,
struct net *net, struct sk_buff *skb,
u32 portid, unsigned int group, gfp_t flags)
{
if (group >= family->n_mcgrps)
return -EINVAL;
group = family->mcgrp_offset + group;
return nlmsg_multicast(net->genl_sock, skb, portid, group, flags);
}

Expand All @@ -266,13 +276,16 @@ static inline int genlmsg_multicast_netns(struct genl_family *family,
* @family: the generic netlink family
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: multicast group id
* @group: offset of multicast group in groups array
* @flags: allocation flags
*/
static inline int genlmsg_multicast(struct genl_family *family,
struct sk_buff *skb, u32 portid,
unsigned int group, gfp_t flags)
{
if (group >= family->n_mcgrps)
return -EINVAL;
group = family->mcgrp_offset + group;
return genlmsg_multicast_netns(family, &init_net, skb,
portid, group, flags);
}
Expand All @@ -282,7 +295,7 @@ static inline int genlmsg_multicast(struct genl_family *family,
* @family: the generic netlink family
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: multicast group id
* @group: offset of multicast group in groups array
* @flags: allocation flags
*
* This function must hold the RTNL or rcu_read_lock().
Expand Down Expand Up @@ -365,6 +378,7 @@ static inline struct sk_buff *genlmsg_new(size_t payload, gfp_t flags)
* @net: the network namespace to report the error to
* @portid: the PORTID of a process that we want to skip (if any)
* @group: the broadcast group that will notice the error
* (this is the offset of the multicast group in the groups array)
* @code: error code, must be negative (as usual in kernelspace)
*
* This function returns the number of broadcast listeners that have set the
Expand Down
Loading

0 comments on commit 2a94fe4

Please sign in to comment.