162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NETLINK Generic Netlink Family 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Jamal Hadi Salim 662306a36Sopenharmony_ci * Thomas Graf <tgraf@suug.ch> 762306a36Sopenharmony_ci * Johannes Berg <johannes@sipsolutions.net> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/socket.h> 1662306a36Sopenharmony_ci#include <linux/string_helpers.h> 1762306a36Sopenharmony_ci#include <linux/skbuff.h> 1862306a36Sopenharmony_ci#include <linux/mutex.h> 1962306a36Sopenharmony_ci#include <linux/bitmap.h> 2062306a36Sopenharmony_ci#include <linux/rwsem.h> 2162306a36Sopenharmony_ci#include <linux/idr.h> 2262306a36Sopenharmony_ci#include <net/sock.h> 2362306a36Sopenharmony_ci#include <net/genetlink.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ 2662306a36Sopenharmony_cistatic DECLARE_RWSEM(cb_lock); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciatomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0); 2962306a36Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid genl_lock(void) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci mutex_lock(&genl_mutex); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ciEXPORT_SYMBOL(genl_lock); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_civoid genl_unlock(void) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci mutex_unlock(&genl_mutex); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL(genl_unlock); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void genl_lock_all(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci down_write(&cb_lock); 4662306a36Sopenharmony_ci genl_lock(); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void genl_unlock_all(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci genl_unlock(); 5262306a36Sopenharmony_ci up_write(&cb_lock); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void genl_op_lock(const struct genl_family *family) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci if (!family->parallel_ops) 5862306a36Sopenharmony_ci genl_lock(); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void genl_op_unlock(const struct genl_family *family) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci if (!family->parallel_ops) 6462306a36Sopenharmony_ci genl_unlock(); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic DEFINE_IDR(genl_fam_idr); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Bitmap of multicast groups that are currently in use. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * To avoid an allocation at boot of just one unsigned long, 7362306a36Sopenharmony_ci * declare it global instead. 7462306a36Sopenharmony_ci * Bit 0 is marked as already used since group 0 is invalid. 7562306a36Sopenharmony_ci * Bit 1 is marked as already used since the drop-monitor code 7662306a36Sopenharmony_ci * abuses the API and thinks it can statically use group 1. 7762306a36Sopenharmony_ci * That group will typically conflict with other groups that 7862306a36Sopenharmony_ci * any proper users use. 7962306a36Sopenharmony_ci * Bit 16 is marked as used since it's used for generic netlink 8062306a36Sopenharmony_ci * and the code no longer marks pre-reserved IDs as used. 8162306a36Sopenharmony_ci * Bit 17 is marked as already used since the VFS quota code 8262306a36Sopenharmony_ci * also abused this API and relied on family == group ID, we 8362306a36Sopenharmony_ci * cater to that by giving it a static family and group ID. 8462306a36Sopenharmony_ci * Bit 18 is marked as already used since the PMCRAID driver 8562306a36Sopenharmony_ci * did the same thing as the VFS quota code (maybe copied?) 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | 8862306a36Sopenharmony_ci BIT(GENL_ID_VFS_DQUOT) | 8962306a36Sopenharmony_ci BIT(GENL_ID_PMCRAID); 9062306a36Sopenharmony_cistatic unsigned long *mc_groups = &mc_group_start; 9162306a36Sopenharmony_cistatic unsigned long mc_groups_longs = 1; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* We need the last attribute with non-zero ID therefore a 2-entry array */ 9462306a36Sopenharmony_cistatic struct nla_policy genl_policy_reject_all[] = { 9562306a36Sopenharmony_ci { .type = NLA_REJECT }, 9662306a36Sopenharmony_ci { .type = NLA_REJECT }, 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int genl_ctrl_event(int event, const struct genl_family *family, 10062306a36Sopenharmony_ci const struct genl_multicast_group *grp, 10162306a36Sopenharmony_ci int grp_id); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void 10462306a36Sopenharmony_cigenl_op_fill_in_reject_policy(const struct genl_family *family, 10562306a36Sopenharmony_ci struct genl_ops *op) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(genl_policy_reject_all) - 1 != 1); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (op->policy || op->cmd < family->resv_start_op) 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci op->policy = genl_policy_reject_all; 11362306a36Sopenharmony_ci op->maxattr = 1; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void 11762306a36Sopenharmony_cigenl_op_fill_in_reject_policy_split(const struct genl_family *family, 11862306a36Sopenharmony_ci struct genl_split_ops *op) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (op->policy) 12162306a36Sopenharmony_ci return; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci op->policy = genl_policy_reject_all; 12462306a36Sopenharmony_ci op->maxattr = 1; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct genl_family *genl_family_find_byid(unsigned int id) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return idr_find(&genl_fam_idr, id); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct genl_family *genl_family_find_byname(char *name) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci const struct genl_family *family; 13562306a36Sopenharmony_ci unsigned int id; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, family, id) 13862306a36Sopenharmony_ci if (strcmp(family->name, name) == 0) 13962306a36Sopenharmony_ci return family; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct genl_op_iter { 14562306a36Sopenharmony_ci const struct genl_family *family; 14662306a36Sopenharmony_ci struct genl_split_ops doit; 14762306a36Sopenharmony_ci struct genl_split_ops dumpit; 14862306a36Sopenharmony_ci int cmd_idx; 14962306a36Sopenharmony_ci int entry_idx; 15062306a36Sopenharmony_ci u32 cmd; 15162306a36Sopenharmony_ci u8 flags; 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void genl_op_from_full(const struct genl_family *family, 15562306a36Sopenharmony_ci unsigned int i, struct genl_ops *op) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci *op = family->ops[i]; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!op->maxattr) 16062306a36Sopenharmony_ci op->maxattr = family->maxattr; 16162306a36Sopenharmony_ci if (!op->policy) 16262306a36Sopenharmony_ci op->policy = family->policy; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci genl_op_fill_in_reject_policy(family, op); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int genl_get_cmd_full(u32 cmd, const struct genl_family *family, 16862306a36Sopenharmony_ci struct genl_ops *op) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int i; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (i = 0; i < family->n_ops; i++) 17362306a36Sopenharmony_ci if (family->ops[i].cmd == cmd) { 17462306a36Sopenharmony_ci genl_op_from_full(family, i, op); 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return -ENOENT; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void genl_op_from_small(const struct genl_family *family, 18262306a36Sopenharmony_ci unsigned int i, struct genl_ops *op) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci memset(op, 0, sizeof(*op)); 18562306a36Sopenharmony_ci op->doit = family->small_ops[i].doit; 18662306a36Sopenharmony_ci op->dumpit = family->small_ops[i].dumpit; 18762306a36Sopenharmony_ci op->cmd = family->small_ops[i].cmd; 18862306a36Sopenharmony_ci op->internal_flags = family->small_ops[i].internal_flags; 18962306a36Sopenharmony_ci op->flags = family->small_ops[i].flags; 19062306a36Sopenharmony_ci op->validate = family->small_ops[i].validate; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci op->maxattr = family->maxattr; 19362306a36Sopenharmony_ci op->policy = family->policy; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci genl_op_fill_in_reject_policy(family, op); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int genl_get_cmd_small(u32 cmd, const struct genl_family *family, 19962306a36Sopenharmony_ci struct genl_ops *op) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int i; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (i = 0; i < family->n_small_ops; i++) 20462306a36Sopenharmony_ci if (family->small_ops[i].cmd == cmd) { 20562306a36Sopenharmony_ci genl_op_from_small(family, i, op); 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return -ENOENT; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void genl_op_from_split(struct genl_op_iter *iter) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci const struct genl_family *family = iter->family; 21562306a36Sopenharmony_ci int i, cnt = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci i = iter->entry_idx - family->n_ops - family->n_small_ops; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (family->split_ops[i + cnt].flags & GENL_CMD_CAP_DO) { 22062306a36Sopenharmony_ci iter->doit = family->split_ops[i + cnt]; 22162306a36Sopenharmony_ci genl_op_fill_in_reject_policy_split(family, &iter->doit); 22262306a36Sopenharmony_ci cnt++; 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci memset(&iter->doit, 0, sizeof(iter->doit)); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (i + cnt < family->n_split_ops && 22862306a36Sopenharmony_ci family->split_ops[i + cnt].flags & GENL_CMD_CAP_DUMP) { 22962306a36Sopenharmony_ci iter->dumpit = family->split_ops[i + cnt]; 23062306a36Sopenharmony_ci genl_op_fill_in_reject_policy_split(family, &iter->dumpit); 23162306a36Sopenharmony_ci cnt++; 23262306a36Sopenharmony_ci } else { 23362306a36Sopenharmony_ci memset(&iter->dumpit, 0, sizeof(iter->dumpit)); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci WARN_ON(!cnt); 23762306a36Sopenharmony_ci iter->entry_idx += cnt; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int 24162306a36Sopenharmony_cigenl_get_cmd_split(u32 cmd, u8 flag, const struct genl_family *family, 24262306a36Sopenharmony_ci struct genl_split_ops *op) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci int i; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci for (i = 0; i < family->n_split_ops; i++) 24762306a36Sopenharmony_ci if (family->split_ops[i].cmd == cmd && 24862306a36Sopenharmony_ci family->split_ops[i].flags & flag) { 24962306a36Sopenharmony_ci *op = family->split_ops[i]; 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return -ENOENT; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int 25762306a36Sopenharmony_cigenl_cmd_full_to_split(struct genl_split_ops *op, 25862306a36Sopenharmony_ci const struct genl_family *family, 25962306a36Sopenharmony_ci const struct genl_ops *full, u8 flags) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci if ((flags & GENL_CMD_CAP_DO && !full->doit) || 26262306a36Sopenharmony_ci (flags & GENL_CMD_CAP_DUMP && !full->dumpit)) { 26362306a36Sopenharmony_ci memset(op, 0, sizeof(*op)); 26462306a36Sopenharmony_ci return -ENOENT; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (flags & GENL_CMD_CAP_DUMP) { 26862306a36Sopenharmony_ci op->start = full->start; 26962306a36Sopenharmony_ci op->dumpit = full->dumpit; 27062306a36Sopenharmony_ci op->done = full->done; 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci op->pre_doit = family->pre_doit; 27362306a36Sopenharmony_ci op->doit = full->doit; 27462306a36Sopenharmony_ci op->post_doit = family->post_doit; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (flags & GENL_CMD_CAP_DUMP && 27862306a36Sopenharmony_ci full->validate & GENL_DONT_VALIDATE_DUMP) { 27962306a36Sopenharmony_ci op->policy = NULL; 28062306a36Sopenharmony_ci op->maxattr = 0; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci op->policy = full->policy; 28362306a36Sopenharmony_ci op->maxattr = full->maxattr; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci op->cmd = full->cmd; 28762306a36Sopenharmony_ci op->internal_flags = full->internal_flags; 28862306a36Sopenharmony_ci op->flags = full->flags; 28962306a36Sopenharmony_ci op->validate = full->validate; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Make sure flags include the GENL_CMD_CAP_DO / GENL_CMD_CAP_DUMP */ 29262306a36Sopenharmony_ci op->flags |= flags; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Must make sure that op is initialized to 0 on failure */ 29862306a36Sopenharmony_cistatic int 29962306a36Sopenharmony_cigenl_get_cmd(u32 cmd, u8 flags, const struct genl_family *family, 30062306a36Sopenharmony_ci struct genl_split_ops *op) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct genl_ops full; 30362306a36Sopenharmony_ci int err; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci err = genl_get_cmd_full(cmd, family, &full); 30662306a36Sopenharmony_ci if (err == -ENOENT) 30762306a36Sopenharmony_ci err = genl_get_cmd_small(cmd, family, &full); 30862306a36Sopenharmony_ci /* Found one of legacy forms */ 30962306a36Sopenharmony_ci if (err == 0) 31062306a36Sopenharmony_ci return genl_cmd_full_to_split(op, family, &full, flags); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci err = genl_get_cmd_split(cmd, flags, family, op); 31362306a36Sopenharmony_ci if (err) 31462306a36Sopenharmony_ci memset(op, 0, sizeof(*op)); 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* For policy dumping only, get ops of both do and dump. 31962306a36Sopenharmony_ci * Fail if both are missing, genl_get_cmd() will zero-init in case of failure. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistatic int 32262306a36Sopenharmony_cigenl_get_cmd_both(u32 cmd, const struct genl_family *family, 32362306a36Sopenharmony_ci struct genl_split_ops *doit, struct genl_split_ops *dumpit) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int err1, err2; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci err1 = genl_get_cmd(cmd, GENL_CMD_CAP_DO, family, doit); 32862306a36Sopenharmony_ci err2 = genl_get_cmd(cmd, GENL_CMD_CAP_DUMP, family, dumpit); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return err1 && err2 ? -ENOENT : 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic bool 33462306a36Sopenharmony_cigenl_op_iter_init(const struct genl_family *family, struct genl_op_iter *iter) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci iter->family = family; 33762306a36Sopenharmony_ci iter->cmd_idx = 0; 33862306a36Sopenharmony_ci iter->entry_idx = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci iter->flags = 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return iter->family->n_ops + 34362306a36Sopenharmony_ci iter->family->n_small_ops + 34462306a36Sopenharmony_ci iter->family->n_split_ops; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic bool genl_op_iter_next(struct genl_op_iter *iter) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci const struct genl_family *family = iter->family; 35062306a36Sopenharmony_ci bool legacy_op = true; 35162306a36Sopenharmony_ci struct genl_ops op; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (iter->entry_idx < family->n_ops) { 35462306a36Sopenharmony_ci genl_op_from_full(family, iter->entry_idx, &op); 35562306a36Sopenharmony_ci } else if (iter->entry_idx < family->n_ops + family->n_small_ops) { 35662306a36Sopenharmony_ci genl_op_from_small(family, iter->entry_idx - family->n_ops, 35762306a36Sopenharmony_ci &op); 35862306a36Sopenharmony_ci } else if (iter->entry_idx < 35962306a36Sopenharmony_ci family->n_ops + family->n_small_ops + family->n_split_ops) { 36062306a36Sopenharmony_ci legacy_op = false; 36162306a36Sopenharmony_ci /* updates entry_idx */ 36262306a36Sopenharmony_ci genl_op_from_split(iter); 36362306a36Sopenharmony_ci } else { 36462306a36Sopenharmony_ci return false; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci iter->cmd_idx++; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (legacy_op) { 37062306a36Sopenharmony_ci iter->entry_idx++; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci genl_cmd_full_to_split(&iter->doit, family, 37362306a36Sopenharmony_ci &op, GENL_CMD_CAP_DO); 37462306a36Sopenharmony_ci genl_cmd_full_to_split(&iter->dumpit, family, 37562306a36Sopenharmony_ci &op, GENL_CMD_CAP_DUMP); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci iter->cmd = iter->doit.cmd | iter->dumpit.cmd; 37962306a36Sopenharmony_ci iter->flags = iter->doit.flags | iter->dumpit.flags; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return true; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void 38562306a36Sopenharmony_cigenl_op_iter_copy(struct genl_op_iter *dst, struct genl_op_iter *src) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci *dst = *src; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic unsigned int genl_op_iter_idx(struct genl_op_iter *iter) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci return iter->cmd_idx; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int genl_allocate_reserve_groups(int n_groups, int *first_id) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci unsigned long *new_groups; 39862306a36Sopenharmony_ci int start = 0; 39962306a36Sopenharmony_ci int i; 40062306a36Sopenharmony_ci int id; 40162306a36Sopenharmony_ci bool fits; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci do { 40462306a36Sopenharmony_ci if (start == 0) 40562306a36Sopenharmony_ci id = find_first_zero_bit(mc_groups, 40662306a36Sopenharmony_ci mc_groups_longs * 40762306a36Sopenharmony_ci BITS_PER_LONG); 40862306a36Sopenharmony_ci else 40962306a36Sopenharmony_ci id = find_next_zero_bit(mc_groups, 41062306a36Sopenharmony_ci mc_groups_longs * BITS_PER_LONG, 41162306a36Sopenharmony_ci start); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci fits = true; 41462306a36Sopenharmony_ci for (i = id; 41562306a36Sopenharmony_ci i < min_t(int, id + n_groups, 41662306a36Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 41762306a36Sopenharmony_ci i++) { 41862306a36Sopenharmony_ci if (test_bit(i, mc_groups)) { 41962306a36Sopenharmony_ci start = i; 42062306a36Sopenharmony_ci fits = false; 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (id + n_groups > mc_groups_longs * BITS_PER_LONG) { 42662306a36Sopenharmony_ci unsigned long new_longs = mc_groups_longs + 42762306a36Sopenharmony_ci BITS_TO_LONGS(n_groups); 42862306a36Sopenharmony_ci size_t nlen = new_longs * sizeof(unsigned long); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (mc_groups == &mc_group_start) { 43162306a36Sopenharmony_ci new_groups = kzalloc(nlen, GFP_KERNEL); 43262306a36Sopenharmony_ci if (!new_groups) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci mc_groups = new_groups; 43562306a36Sopenharmony_ci *mc_groups = mc_group_start; 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci new_groups = krealloc(mc_groups, nlen, 43862306a36Sopenharmony_ci GFP_KERNEL); 43962306a36Sopenharmony_ci if (!new_groups) 44062306a36Sopenharmony_ci return -ENOMEM; 44162306a36Sopenharmony_ci mc_groups = new_groups; 44262306a36Sopenharmony_ci for (i = 0; i < BITS_TO_LONGS(n_groups); i++) 44362306a36Sopenharmony_ci mc_groups[mc_groups_longs + i] = 0; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci mc_groups_longs = new_longs; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } while (!fits); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (i = id; i < id + n_groups; i++) 45062306a36Sopenharmony_ci set_bit(i, mc_groups); 45162306a36Sopenharmony_ci *first_id = id; 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic struct genl_family genl_ctrl; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int genl_validate_assign_mc_groups(struct genl_family *family) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int first_id; 46062306a36Sopenharmony_ci int n_groups = family->n_mcgrps; 46162306a36Sopenharmony_ci int err = 0, i; 46262306a36Sopenharmony_ci bool groups_allocated = false; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!n_groups) 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci for (i = 0; i < n_groups; i++) { 46862306a36Sopenharmony_ci const struct genl_multicast_group *grp = &family->mcgrps[i]; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (WARN_ON(grp->name[0] == '\0')) 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci if (WARN_ON(!string_is_terminated(grp->name, GENL_NAMSIZ))) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* special-case our own group and hacks */ 47762306a36Sopenharmony_ci if (family == &genl_ctrl) { 47862306a36Sopenharmony_ci first_id = GENL_ID_CTRL; 47962306a36Sopenharmony_ci BUG_ON(n_groups != 1); 48062306a36Sopenharmony_ci } else if (strcmp(family->name, "NET_DM") == 0) { 48162306a36Sopenharmony_ci first_id = 1; 48262306a36Sopenharmony_ci BUG_ON(n_groups != 1); 48362306a36Sopenharmony_ci } else if (family->id == GENL_ID_VFS_DQUOT) { 48462306a36Sopenharmony_ci first_id = GENL_ID_VFS_DQUOT; 48562306a36Sopenharmony_ci BUG_ON(n_groups != 1); 48662306a36Sopenharmony_ci } else if (family->id == GENL_ID_PMCRAID) { 48762306a36Sopenharmony_ci first_id = GENL_ID_PMCRAID; 48862306a36Sopenharmony_ci BUG_ON(n_groups != 1); 48962306a36Sopenharmony_ci } else { 49062306a36Sopenharmony_ci groups_allocated = true; 49162306a36Sopenharmony_ci err = genl_allocate_reserve_groups(n_groups, &first_id); 49262306a36Sopenharmony_ci if (err) 49362306a36Sopenharmony_ci return err; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci family->mcgrp_offset = first_id; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* if still initializing, can't and don't need to realloc bitmaps */ 49962306a36Sopenharmony_ci if (!init_net.genl_sock) 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (family->netnsok) { 50362306a36Sopenharmony_ci struct net *net; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci netlink_table_grab(); 50662306a36Sopenharmony_ci rcu_read_lock(); 50762306a36Sopenharmony_ci for_each_net_rcu(net) { 50862306a36Sopenharmony_ci err = __netlink_change_ngroups(net->genl_sock, 50962306a36Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 51062306a36Sopenharmony_ci if (err) { 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * No need to roll back, can only fail if 51362306a36Sopenharmony_ci * memory allocation fails and then the 51462306a36Sopenharmony_ci * number of _possible_ groups has been 51562306a36Sopenharmony_ci * increased on some sockets which is ok. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci rcu_read_unlock(); 52162306a36Sopenharmony_ci netlink_table_ungrab(); 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci err = netlink_change_ngroups(init_net.genl_sock, 52462306a36Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (groups_allocated && err) { 52862306a36Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 52962306a36Sopenharmony_ci clear_bit(family->mcgrp_offset + i, mc_groups); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return err; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void genl_unregister_mc_groups(const struct genl_family *family) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct net *net; 53862306a36Sopenharmony_ci int i; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci netlink_table_grab(); 54162306a36Sopenharmony_ci rcu_read_lock(); 54262306a36Sopenharmony_ci for_each_net_rcu(net) { 54362306a36Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 54462306a36Sopenharmony_ci __netlink_clear_multicast_users( 54562306a36Sopenharmony_ci net->genl_sock, family->mcgrp_offset + i); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci rcu_read_unlock(); 54862306a36Sopenharmony_ci netlink_table_ungrab(); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) { 55162306a36Sopenharmony_ci int grp_id = family->mcgrp_offset + i; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (grp_id != 1) 55462306a36Sopenharmony_ci clear_bit(grp_id, mc_groups); 55562306a36Sopenharmony_ci genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, 55662306a36Sopenharmony_ci &family->mcgrps[i], grp_id); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic bool genl_split_op_check(const struct genl_split_ops *op) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci if (WARN_ON(hweight8(op->flags & (GENL_CMD_CAP_DO | 56362306a36Sopenharmony_ci GENL_CMD_CAP_DUMP)) != 1)) 56462306a36Sopenharmony_ci return true; 56562306a36Sopenharmony_ci return false; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int genl_validate_ops(const struct genl_family *family) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct genl_op_iter i, j; 57162306a36Sopenharmony_ci unsigned int s; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (WARN_ON(family->n_ops && !family->ops) || 57462306a36Sopenharmony_ci WARN_ON(family->n_small_ops && !family->small_ops) || 57562306a36Sopenharmony_ci WARN_ON(family->n_split_ops && !family->split_ops)) 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci for (genl_op_iter_init(family, &i); genl_op_iter_next(&i); ) { 57962306a36Sopenharmony_ci if (!(i.flags & (GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP))) 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (WARN_ON(i.cmd >= family->resv_start_op && 58362306a36Sopenharmony_ci (i.doit.validate || i.dumpit.validate))) 58462306a36Sopenharmony_ci return -EINVAL; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci genl_op_iter_copy(&j, &i); 58762306a36Sopenharmony_ci while (genl_op_iter_next(&j)) { 58862306a36Sopenharmony_ci if (i.cmd == j.cmd) 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (family->n_split_ops) { 59462306a36Sopenharmony_ci if (genl_split_op_check(&family->split_ops[0])) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci for (s = 1; s < family->n_split_ops; s++) { 59962306a36Sopenharmony_ci const struct genl_split_ops *a, *b; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci a = &family->split_ops[s - 1]; 60262306a36Sopenharmony_ci b = &family->split_ops[s]; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (genl_split_op_check(b)) 60562306a36Sopenharmony_ci return -EINVAL; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Check sort order */ 60862306a36Sopenharmony_ci if (a->cmd < b->cmd) { 60962306a36Sopenharmony_ci continue; 61062306a36Sopenharmony_ci } else if (a->cmd > b->cmd) { 61162306a36Sopenharmony_ci WARN_ON(1); 61262306a36Sopenharmony_ci return -EINVAL; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (a->internal_flags != b->internal_flags || 61662306a36Sopenharmony_ci ((a->flags ^ b->flags) & ~(GENL_CMD_CAP_DO | 61762306a36Sopenharmony_ci GENL_CMD_CAP_DUMP))) { 61862306a36Sopenharmony_ci WARN_ON(1); 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if ((a->flags & GENL_CMD_CAP_DO) && 62362306a36Sopenharmony_ci (b->flags & GENL_CMD_CAP_DUMP)) 62462306a36Sopenharmony_ci continue; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci WARN_ON(1); 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/** 63462306a36Sopenharmony_ci * genl_register_family - register a generic netlink family 63562306a36Sopenharmony_ci * @family: generic netlink family 63662306a36Sopenharmony_ci * 63762306a36Sopenharmony_ci * Registers the specified family after validating it first. Only one 63862306a36Sopenharmony_ci * family may be registered with the same family name or identifier. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * The family's ops, multicast groups and module pointer must already 64162306a36Sopenharmony_ci * be assigned. 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * Return 0 on success or a negative error code. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ciint genl_register_family(struct genl_family *family) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci int err, i; 64862306a36Sopenharmony_ci int start = GENL_START_ALLOC, end = GENL_MAX_ID; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci err = genl_validate_ops(family); 65162306a36Sopenharmony_ci if (err) 65262306a36Sopenharmony_ci return err; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci genl_lock_all(); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (genl_family_find_byname(family->name)) { 65762306a36Sopenharmony_ci err = -EEXIST; 65862306a36Sopenharmony_ci goto errout_locked; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* 66262306a36Sopenharmony_ci * Sadly, a few cases need to be special-cased 66362306a36Sopenharmony_ci * due to them having previously abused the API 66462306a36Sopenharmony_ci * and having used their family ID also as their 66562306a36Sopenharmony_ci * multicast group ID, so we use reserved IDs 66662306a36Sopenharmony_ci * for both to be sure we can do that mapping. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci if (family == &genl_ctrl) { 66962306a36Sopenharmony_ci /* and this needs to be special for initial family lookups */ 67062306a36Sopenharmony_ci start = end = GENL_ID_CTRL; 67162306a36Sopenharmony_ci } else if (strcmp(family->name, "pmcraid") == 0) { 67262306a36Sopenharmony_ci start = end = GENL_ID_PMCRAID; 67362306a36Sopenharmony_ci } else if (strcmp(family->name, "VFS_DQUOT") == 0) { 67462306a36Sopenharmony_ci start = end = GENL_ID_VFS_DQUOT; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci family->id = idr_alloc_cyclic(&genl_fam_idr, family, 67862306a36Sopenharmony_ci start, end + 1, GFP_KERNEL); 67962306a36Sopenharmony_ci if (family->id < 0) { 68062306a36Sopenharmony_ci err = family->id; 68162306a36Sopenharmony_ci goto errout_locked; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci err = genl_validate_assign_mc_groups(family); 68562306a36Sopenharmony_ci if (err) 68662306a36Sopenharmony_ci goto errout_remove; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci genl_unlock_all(); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* send all events */ 69162306a36Sopenharmony_ci genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0); 69262306a36Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 69362306a36Sopenharmony_ci genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, 69462306a36Sopenharmony_ci &family->mcgrps[i], family->mcgrp_offset + i); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cierrout_remove: 69962306a36Sopenharmony_ci idr_remove(&genl_fam_idr, family->id); 70062306a36Sopenharmony_cierrout_locked: 70162306a36Sopenharmony_ci genl_unlock_all(); 70262306a36Sopenharmony_ci return err; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ciEXPORT_SYMBOL(genl_register_family); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/** 70762306a36Sopenharmony_ci * genl_unregister_family - unregister generic netlink family 70862306a36Sopenharmony_ci * @family: generic netlink family 70962306a36Sopenharmony_ci * 71062306a36Sopenharmony_ci * Unregisters the specified family. 71162306a36Sopenharmony_ci * 71262306a36Sopenharmony_ci * Returns 0 on success or a negative error code. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ciint genl_unregister_family(const struct genl_family *family) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci genl_lock_all(); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!genl_family_find_byid(family->id)) { 71962306a36Sopenharmony_ci genl_unlock_all(); 72062306a36Sopenharmony_ci return -ENOENT; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci genl_unregister_mc_groups(family); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci idr_remove(&genl_fam_idr, family->id); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci up_write(&cb_lock); 72862306a36Sopenharmony_ci wait_event(genl_sk_destructing_waitq, 72962306a36Sopenharmony_ci atomic_read(&genl_sk_destructing_cnt) == 0); 73062306a36Sopenharmony_ci genl_unlock(); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ciEXPORT_SYMBOL(genl_unregister_family); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/** 73962306a36Sopenharmony_ci * genlmsg_put - Add generic netlink header to netlink message 74062306a36Sopenharmony_ci * @skb: socket buffer holding the message 74162306a36Sopenharmony_ci * @portid: netlink portid the message is addressed to 74262306a36Sopenharmony_ci * @seq: sequence number (usually the one of the sender) 74362306a36Sopenharmony_ci * @family: generic netlink family 74462306a36Sopenharmony_ci * @flags: netlink message flags 74562306a36Sopenharmony_ci * @cmd: generic netlink command 74662306a36Sopenharmony_ci * 74762306a36Sopenharmony_ci * Returns pointer to user specific header 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_civoid *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, 75062306a36Sopenharmony_ci const struct genl_family *family, int flags, u8 cmd) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct nlmsghdr *nlh; 75362306a36Sopenharmony_ci struct genlmsghdr *hdr; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN + 75662306a36Sopenharmony_ci family->hdrsize, flags); 75762306a36Sopenharmony_ci if (nlh == NULL) 75862306a36Sopenharmony_ci return NULL; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci hdr = nlmsg_data(nlh); 76162306a36Sopenharmony_ci hdr->cmd = cmd; 76262306a36Sopenharmony_ci hdr->version = family->version; 76362306a36Sopenharmony_ci hdr->reserved = 0; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return (char *) hdr + GENL_HDRLEN; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL(genlmsg_put); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic struct genl_dumpit_info *genl_dumpit_info_alloc(void) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci return kmalloc(sizeof(struct genl_dumpit_info), GFP_KERNEL); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void genl_dumpit_info_free(const struct genl_dumpit_info *info) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci kfree(info); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic struct nlattr ** 78062306a36Sopenharmony_cigenl_family_rcv_msg_attrs_parse(const struct genl_family *family, 78162306a36Sopenharmony_ci struct nlmsghdr *nlh, 78262306a36Sopenharmony_ci struct netlink_ext_ack *extack, 78362306a36Sopenharmony_ci const struct genl_split_ops *ops, 78462306a36Sopenharmony_ci int hdrlen, 78562306a36Sopenharmony_ci enum genl_validate_flags no_strict_flag) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci enum netlink_validation validate = ops->validate & no_strict_flag ? 78862306a36Sopenharmony_ci NL_VALIDATE_LIBERAL : 78962306a36Sopenharmony_ci NL_VALIDATE_STRICT; 79062306a36Sopenharmony_ci struct nlattr **attrbuf; 79162306a36Sopenharmony_ci int err; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!ops->maxattr) 79462306a36Sopenharmony_ci return NULL; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci attrbuf = kmalloc_array(ops->maxattr + 1, 79762306a36Sopenharmony_ci sizeof(struct nlattr *), GFP_KERNEL); 79862306a36Sopenharmony_ci if (!attrbuf) 79962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci err = __nlmsg_parse(nlh, hdrlen, attrbuf, ops->maxattr, ops->policy, 80262306a36Sopenharmony_ci validate, extack); 80362306a36Sopenharmony_ci if (err) { 80462306a36Sopenharmony_ci kfree(attrbuf); 80562306a36Sopenharmony_ci return ERR_PTR(err); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci return attrbuf; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic void genl_family_rcv_msg_attrs_free(struct nlattr **attrbuf) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci kfree(attrbuf); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistruct genl_start_context { 81662306a36Sopenharmony_ci const struct genl_family *family; 81762306a36Sopenharmony_ci struct nlmsghdr *nlh; 81862306a36Sopenharmony_ci struct netlink_ext_ack *extack; 81962306a36Sopenharmony_ci const struct genl_split_ops *ops; 82062306a36Sopenharmony_ci int hdrlen; 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int genl_start(struct netlink_callback *cb) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct genl_start_context *ctx = cb->data; 82662306a36Sopenharmony_ci const struct genl_split_ops *ops; 82762306a36Sopenharmony_ci struct genl_dumpit_info *info; 82862306a36Sopenharmony_ci struct nlattr **attrs = NULL; 82962306a36Sopenharmony_ci int rc = 0; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ops = ctx->ops; 83262306a36Sopenharmony_ci if (!(ops->validate & GENL_DONT_VALIDATE_DUMP) && 83362306a36Sopenharmony_ci ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) 83462306a36Sopenharmony_ci return -EINVAL; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack, 83762306a36Sopenharmony_ci ops, ctx->hdrlen, 83862306a36Sopenharmony_ci GENL_DONT_VALIDATE_DUMP_STRICT); 83962306a36Sopenharmony_ci if (IS_ERR(attrs)) 84062306a36Sopenharmony_ci return PTR_ERR(attrs); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci info = genl_dumpit_info_alloc(); 84362306a36Sopenharmony_ci if (!info) { 84462306a36Sopenharmony_ci genl_family_rcv_msg_attrs_free(attrs); 84562306a36Sopenharmony_ci return -ENOMEM; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci info->op = *ops; 84862306a36Sopenharmony_ci info->info.family = ctx->family; 84962306a36Sopenharmony_ci info->info.snd_seq = cb->nlh->nlmsg_seq; 85062306a36Sopenharmony_ci info->info.snd_portid = NETLINK_CB(cb->skb).portid; 85162306a36Sopenharmony_ci info->info.nlhdr = cb->nlh; 85262306a36Sopenharmony_ci info->info.genlhdr = nlmsg_data(cb->nlh); 85362306a36Sopenharmony_ci info->info.attrs = attrs; 85462306a36Sopenharmony_ci genl_info_net_set(&info->info, sock_net(cb->skb->sk)); 85562306a36Sopenharmony_ci info->info.extack = cb->extack; 85662306a36Sopenharmony_ci memset(&info->info.user_ptr, 0, sizeof(info->info.user_ptr)); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci cb->data = info; 85962306a36Sopenharmony_ci if (ops->start) { 86062306a36Sopenharmony_ci genl_op_lock(ctx->family); 86162306a36Sopenharmony_ci rc = ops->start(cb); 86262306a36Sopenharmony_ci genl_op_unlock(ctx->family); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (rc) { 86662306a36Sopenharmony_ci genl_family_rcv_msg_attrs_free(info->info.attrs); 86762306a36Sopenharmony_ci genl_dumpit_info_free(info); 86862306a36Sopenharmony_ci cb->data = NULL; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci return rc; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int genl_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct genl_dumpit_info *dump_info = cb->data; 87662306a36Sopenharmony_ci const struct genl_split_ops *ops = &dump_info->op; 87762306a36Sopenharmony_ci struct genl_info *info = &dump_info->info; 87862306a36Sopenharmony_ci int rc; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci info->extack = cb->extack; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci genl_op_lock(info->family); 88362306a36Sopenharmony_ci rc = ops->dumpit(skb, cb); 88462306a36Sopenharmony_ci genl_op_unlock(info->family); 88562306a36Sopenharmony_ci return rc; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int genl_done(struct netlink_callback *cb) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct genl_dumpit_info *dump_info = cb->data; 89162306a36Sopenharmony_ci const struct genl_split_ops *ops = &dump_info->op; 89262306a36Sopenharmony_ci struct genl_info *info = &dump_info->info; 89362306a36Sopenharmony_ci int rc = 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci info->extack = cb->extack; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (ops->done) { 89862306a36Sopenharmony_ci genl_op_lock(info->family); 89962306a36Sopenharmony_ci rc = ops->done(cb); 90062306a36Sopenharmony_ci genl_op_unlock(info->family); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci genl_family_rcv_msg_attrs_free(info->attrs); 90362306a36Sopenharmony_ci genl_dumpit_info_free(dump_info); 90462306a36Sopenharmony_ci return rc; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int genl_family_rcv_msg_dumpit(const struct genl_family *family, 90862306a36Sopenharmony_ci struct sk_buff *skb, 90962306a36Sopenharmony_ci struct nlmsghdr *nlh, 91062306a36Sopenharmony_ci struct netlink_ext_ack *extack, 91162306a36Sopenharmony_ci const struct genl_split_ops *ops, 91262306a36Sopenharmony_ci int hdrlen, struct net *net) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct genl_start_context ctx; 91562306a36Sopenharmony_ci struct netlink_dump_control c = { 91662306a36Sopenharmony_ci .module = family->module, 91762306a36Sopenharmony_ci .data = &ctx, 91862306a36Sopenharmony_ci .start = genl_start, 91962306a36Sopenharmony_ci .dump = genl_dumpit, 92062306a36Sopenharmony_ci .done = genl_done, 92162306a36Sopenharmony_ci .extack = extack, 92262306a36Sopenharmony_ci }; 92362306a36Sopenharmony_ci int err; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci ctx.family = family; 92662306a36Sopenharmony_ci ctx.nlh = nlh; 92762306a36Sopenharmony_ci ctx.extack = extack; 92862306a36Sopenharmony_ci ctx.ops = ops; 92962306a36Sopenharmony_ci ctx.hdrlen = hdrlen; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci genl_op_unlock(family); 93262306a36Sopenharmony_ci err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); 93362306a36Sopenharmony_ci genl_op_lock(family); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return err; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int genl_family_rcv_msg_doit(const struct genl_family *family, 93962306a36Sopenharmony_ci struct sk_buff *skb, 94062306a36Sopenharmony_ci struct nlmsghdr *nlh, 94162306a36Sopenharmony_ci struct netlink_ext_ack *extack, 94262306a36Sopenharmony_ci const struct genl_split_ops *ops, 94362306a36Sopenharmony_ci int hdrlen, struct net *net) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct nlattr **attrbuf; 94662306a36Sopenharmony_ci struct genl_info info; 94762306a36Sopenharmony_ci int err; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack, 95062306a36Sopenharmony_ci ops, hdrlen, 95162306a36Sopenharmony_ci GENL_DONT_VALIDATE_STRICT); 95262306a36Sopenharmony_ci if (IS_ERR(attrbuf)) 95362306a36Sopenharmony_ci return PTR_ERR(attrbuf); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci info.snd_seq = nlh->nlmsg_seq; 95662306a36Sopenharmony_ci info.snd_portid = NETLINK_CB(skb).portid; 95762306a36Sopenharmony_ci info.family = family; 95862306a36Sopenharmony_ci info.nlhdr = nlh; 95962306a36Sopenharmony_ci info.genlhdr = nlmsg_data(nlh); 96062306a36Sopenharmony_ci info.attrs = attrbuf; 96162306a36Sopenharmony_ci info.extack = extack; 96262306a36Sopenharmony_ci genl_info_net_set(&info, net); 96362306a36Sopenharmony_ci memset(&info.user_ptr, 0, sizeof(info.user_ptr)); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (ops->pre_doit) { 96662306a36Sopenharmony_ci err = ops->pre_doit(ops, skb, &info); 96762306a36Sopenharmony_ci if (err) 96862306a36Sopenharmony_ci goto out; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci err = ops->doit(skb, &info); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (ops->post_doit) 97462306a36Sopenharmony_ci ops->post_doit(ops, skb, &info); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciout: 97762306a36Sopenharmony_ci genl_family_rcv_msg_attrs_free(attrbuf); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return err; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic int genl_header_check(const struct genl_family *family, 98362306a36Sopenharmony_ci struct nlmsghdr *nlh, struct genlmsghdr *hdr, 98462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci u16 flags; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* Only for commands added after we started validating */ 98962306a36Sopenharmony_ci if (hdr->cmd < family->resv_start_op) 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (hdr->reserved) { 99362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "genlmsghdr.reserved field is not 0"); 99462306a36Sopenharmony_ci return -EINVAL; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Old netlink flags have pretty loose semantics, allow only the flags 99862306a36Sopenharmony_ci * consumed by the core where we can enforce the meaning. 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci flags = nlh->nlmsg_flags; 100162306a36Sopenharmony_ci if ((flags & NLM_F_DUMP) == NLM_F_DUMP) /* DUMP is 2 bits */ 100262306a36Sopenharmony_ci flags &= ~NLM_F_DUMP; 100362306a36Sopenharmony_ci if (flags & ~(NLM_F_REQUEST | NLM_F_ACK | NLM_F_ECHO)) { 100462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 100562306a36Sopenharmony_ci "ambiguous or reserved bits set in nlmsg_flags"); 100662306a36Sopenharmony_ci return -EINVAL; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int genl_family_rcv_msg(const struct genl_family *family, 101362306a36Sopenharmony_ci struct sk_buff *skb, 101462306a36Sopenharmony_ci struct nlmsghdr *nlh, 101562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 101862306a36Sopenharmony_ci struct genlmsghdr *hdr = nlmsg_data(nlh); 101962306a36Sopenharmony_ci struct genl_split_ops op; 102062306a36Sopenharmony_ci int hdrlen; 102162306a36Sopenharmony_ci u8 flags; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* this family doesn't exist in this netns */ 102462306a36Sopenharmony_ci if (!family->netnsok && !net_eq(net, &init_net)) 102562306a36Sopenharmony_ci return -ENOENT; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci hdrlen = GENL_HDRLEN + family->hdrsize; 102862306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) 102962306a36Sopenharmony_ci return -EINVAL; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (genl_header_check(family, nlh, hdr, extack)) 103262306a36Sopenharmony_ci return -EINVAL; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci flags = (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP ? 103562306a36Sopenharmony_ci GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO; 103662306a36Sopenharmony_ci if (genl_get_cmd(hdr->cmd, flags, family, &op)) 103762306a36Sopenharmony_ci return -EOPNOTSUPP; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if ((op.flags & GENL_ADMIN_PERM) && 104062306a36Sopenharmony_ci !netlink_capable(skb, CAP_NET_ADMIN)) 104162306a36Sopenharmony_ci return -EPERM; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if ((op.flags & GENL_UNS_ADMIN_PERM) && 104462306a36Sopenharmony_ci !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 104562306a36Sopenharmony_ci return -EPERM; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (flags & GENL_CMD_CAP_DUMP) 104862306a36Sopenharmony_ci return genl_family_rcv_msg_dumpit(family, skb, nlh, extack, 104962306a36Sopenharmony_ci &op, hdrlen, net); 105062306a36Sopenharmony_ci else 105162306a36Sopenharmony_ci return genl_family_rcv_msg_doit(family, skb, nlh, extack, 105262306a36Sopenharmony_ci &op, hdrlen, net); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, 105662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci const struct genl_family *family; 105962306a36Sopenharmony_ci int err; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci family = genl_family_find_byid(nlh->nlmsg_type); 106262306a36Sopenharmony_ci if (family == NULL) 106362306a36Sopenharmony_ci return -ENOENT; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci genl_op_lock(family); 106662306a36Sopenharmony_ci err = genl_family_rcv_msg(family, skb, nlh, extack); 106762306a36Sopenharmony_ci genl_op_unlock(family); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci return err; 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic void genl_rcv(struct sk_buff *skb) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci down_read(&cb_lock); 107562306a36Sopenharmony_ci netlink_rcv_skb(skb, &genl_rcv_msg); 107662306a36Sopenharmony_ci up_read(&cb_lock); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/************************************************************************** 108062306a36Sopenharmony_ci * Controller 108162306a36Sopenharmony_ci **************************************************************************/ 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic struct genl_family genl_ctrl; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, 108662306a36Sopenharmony_ci u32 flags, struct sk_buff *skb, u8 cmd) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct genl_op_iter i; 108962306a36Sopenharmony_ci void *hdr; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); 109262306a36Sopenharmony_ci if (hdr == NULL) 109362306a36Sopenharmony_ci return -1; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || 109662306a36Sopenharmony_ci nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) || 109762306a36Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_VERSION, family->version) || 109862306a36Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize) || 109962306a36Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) 110062306a36Sopenharmony_ci goto nla_put_failure; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (genl_op_iter_init(family, &i)) { 110362306a36Sopenharmony_ci struct nlattr *nla_ops; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci nla_ops = nla_nest_start_noflag(skb, CTRL_ATTR_OPS); 110662306a36Sopenharmony_ci if (nla_ops == NULL) 110762306a36Sopenharmony_ci goto nla_put_failure; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci while (genl_op_iter_next(&i)) { 111062306a36Sopenharmony_ci struct nlattr *nest; 111162306a36Sopenharmony_ci u32 op_flags; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci op_flags = i.flags; 111462306a36Sopenharmony_ci if (i.doit.policy || i.dumpit.policy) 111562306a36Sopenharmony_ci op_flags |= GENL_CMD_CAP_HASPOL; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, genl_op_iter_idx(&i)); 111862306a36Sopenharmony_ci if (nest == NULL) 111962306a36Sopenharmony_ci goto nla_put_failure; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_OP_ID, i.cmd) || 112262306a36Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags)) 112362306a36Sopenharmony_ci goto nla_put_failure; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci nla_nest_end(skb, nest); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci nla_nest_end(skb, nla_ops); 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (family->n_mcgrps) { 113262306a36Sopenharmony_ci struct nlattr *nla_grps; 113362306a36Sopenharmony_ci int i; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); 113662306a36Sopenharmony_ci if (nla_grps == NULL) 113762306a36Sopenharmony_ci goto nla_put_failure; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) { 114062306a36Sopenharmony_ci struct nlattr *nest; 114162306a36Sopenharmony_ci const struct genl_multicast_group *grp; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci grp = &family->mcgrps[i]; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, i + 1); 114662306a36Sopenharmony_ci if (nest == NULL) 114762306a36Sopenharmony_ci goto nla_put_failure; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, 115062306a36Sopenharmony_ci family->mcgrp_offset + i) || 115162306a36Sopenharmony_ci nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, 115262306a36Sopenharmony_ci grp->name)) 115362306a36Sopenharmony_ci goto nla_put_failure; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci nla_nest_end(skb, nest); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci nla_nest_end(skb, nla_grps); 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci genlmsg_end(skb, hdr); 116162306a36Sopenharmony_ci return 0; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cinla_put_failure: 116462306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 116562306a36Sopenharmony_ci return -EMSGSIZE; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic int ctrl_fill_mcgrp_info(const struct genl_family *family, 116962306a36Sopenharmony_ci const struct genl_multicast_group *grp, 117062306a36Sopenharmony_ci int grp_id, u32 portid, u32 seq, u32 flags, 117162306a36Sopenharmony_ci struct sk_buff *skb, u8 cmd) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci void *hdr; 117462306a36Sopenharmony_ci struct nlattr *nla_grps; 117562306a36Sopenharmony_ci struct nlattr *nest; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); 117862306a36Sopenharmony_ci if (hdr == NULL) 117962306a36Sopenharmony_ci return -1; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || 118262306a36Sopenharmony_ci nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id)) 118362306a36Sopenharmony_ci goto nla_put_failure; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); 118662306a36Sopenharmony_ci if (nla_grps == NULL) 118762306a36Sopenharmony_ci goto nla_put_failure; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, 1); 119062306a36Sopenharmony_ci if (nest == NULL) 119162306a36Sopenharmony_ci goto nla_put_failure; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) || 119462306a36Sopenharmony_ci nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, 119562306a36Sopenharmony_ci grp->name)) 119662306a36Sopenharmony_ci goto nla_put_failure; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci nla_nest_end(skb, nest); 119962306a36Sopenharmony_ci nla_nest_end(skb, nla_grps); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci genlmsg_end(skb, hdr); 120262306a36Sopenharmony_ci return 0; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cinla_put_failure: 120562306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 120662306a36Sopenharmony_ci return -EMSGSIZE; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci int n = 0; 121262306a36Sopenharmony_ci struct genl_family *rt; 121362306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 121462306a36Sopenharmony_ci int fams_to_skip = cb->args[0]; 121562306a36Sopenharmony_ci unsigned int id; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, rt, id) { 121862306a36Sopenharmony_ci if (!rt->netnsok && !net_eq(net, &init_net)) 121962306a36Sopenharmony_ci continue; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (n++ < fams_to_skip) 122262306a36Sopenharmony_ci continue; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, 122562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 122662306a36Sopenharmony_ci skb, CTRL_CMD_NEWFAMILY) < 0) { 122762306a36Sopenharmony_ci n--; 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci cb->args[0] = n; 123362306a36Sopenharmony_ci return skb->len; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic struct sk_buff *ctrl_build_family_msg(const struct genl_family *family, 123762306a36Sopenharmony_ci u32 portid, int seq, u8 cmd) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci struct sk_buff *skb; 124062306a36Sopenharmony_ci int err; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 124362306a36Sopenharmony_ci if (skb == NULL) 124462306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci err = ctrl_fill_info(family, portid, seq, 0, skb, cmd); 124762306a36Sopenharmony_ci if (err < 0) { 124862306a36Sopenharmony_ci nlmsg_free(skb); 124962306a36Sopenharmony_ci return ERR_PTR(err); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return skb; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic struct sk_buff * 125662306a36Sopenharmony_cictrl_build_mcgrp_msg(const struct genl_family *family, 125762306a36Sopenharmony_ci const struct genl_multicast_group *grp, 125862306a36Sopenharmony_ci int grp_id, u32 portid, int seq, u8 cmd) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct sk_buff *skb; 126162306a36Sopenharmony_ci int err; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 126462306a36Sopenharmony_ci if (skb == NULL) 126562306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid, 126862306a36Sopenharmony_ci seq, 0, skb, cmd); 126962306a36Sopenharmony_ci if (err < 0) { 127062306a36Sopenharmony_ci nlmsg_free(skb); 127162306a36Sopenharmony_ci return ERR_PTR(err); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return skb; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic const struct nla_policy ctrl_policy_family[] = { 127862306a36Sopenharmony_ci [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, 127962306a36Sopenharmony_ci [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, 128062306a36Sopenharmony_ci .len = GENL_NAMSIZ - 1 }, 128162306a36Sopenharmony_ci}; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct sk_buff *msg; 128662306a36Sopenharmony_ci const struct genl_family *res = NULL; 128762306a36Sopenharmony_ci int err = -EINVAL; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (info->attrs[CTRL_ATTR_FAMILY_ID]) { 129062306a36Sopenharmony_ci u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); 129162306a36Sopenharmony_ci res = genl_family_find_byid(id); 129262306a36Sopenharmony_ci err = -ENOENT; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { 129662306a36Sopenharmony_ci char *name; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); 129962306a36Sopenharmony_ci res = genl_family_find_byname(name); 130062306a36Sopenharmony_ci#ifdef CONFIG_MODULES 130162306a36Sopenharmony_ci if (res == NULL) { 130262306a36Sopenharmony_ci genl_unlock(); 130362306a36Sopenharmony_ci up_read(&cb_lock); 130462306a36Sopenharmony_ci request_module("net-pf-%d-proto-%d-family-%s", 130562306a36Sopenharmony_ci PF_NETLINK, NETLINK_GENERIC, name); 130662306a36Sopenharmony_ci down_read(&cb_lock); 130762306a36Sopenharmony_ci genl_lock(); 130862306a36Sopenharmony_ci res = genl_family_find_byname(name); 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci#endif 131162306a36Sopenharmony_ci err = -ENOENT; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (res == NULL) 131562306a36Sopenharmony_ci return err; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { 131862306a36Sopenharmony_ci /* family doesn't exist here */ 131962306a36Sopenharmony_ci return -ENOENT; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci msg = ctrl_build_family_msg(res, info->snd_portid, info->snd_seq, 132362306a36Sopenharmony_ci CTRL_CMD_NEWFAMILY); 132462306a36Sopenharmony_ci if (IS_ERR(msg)) 132562306a36Sopenharmony_ci return PTR_ERR(msg); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci return genlmsg_reply(msg, info); 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic int genl_ctrl_event(int event, const struct genl_family *family, 133162306a36Sopenharmony_ci const struct genl_multicast_group *grp, 133262306a36Sopenharmony_ci int grp_id) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci struct sk_buff *msg; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* genl is still initialising */ 133762306a36Sopenharmony_ci if (!init_net.genl_sock) 133862306a36Sopenharmony_ci return 0; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci switch (event) { 134162306a36Sopenharmony_ci case CTRL_CMD_NEWFAMILY: 134262306a36Sopenharmony_ci case CTRL_CMD_DELFAMILY: 134362306a36Sopenharmony_ci WARN_ON(grp); 134462306a36Sopenharmony_ci msg = ctrl_build_family_msg(family, 0, 0, event); 134562306a36Sopenharmony_ci break; 134662306a36Sopenharmony_ci case CTRL_CMD_NEWMCAST_GRP: 134762306a36Sopenharmony_ci case CTRL_CMD_DELMCAST_GRP: 134862306a36Sopenharmony_ci BUG_ON(!grp); 134962306a36Sopenharmony_ci msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event); 135062306a36Sopenharmony_ci break; 135162306a36Sopenharmony_ci default: 135262306a36Sopenharmony_ci return -EINVAL; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (IS_ERR(msg)) 135662306a36Sopenharmony_ci return PTR_ERR(msg); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (!family->netnsok) { 135962306a36Sopenharmony_ci genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, 136062306a36Sopenharmony_ci 0, GFP_KERNEL); 136162306a36Sopenharmony_ci } else { 136262306a36Sopenharmony_ci rcu_read_lock(); 136362306a36Sopenharmony_ci genlmsg_multicast_allns(&genl_ctrl, msg, 0, 136462306a36Sopenharmony_ci 0, GFP_ATOMIC); 136562306a36Sopenharmony_ci rcu_read_unlock(); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistruct ctrl_dump_policy_ctx { 137262306a36Sopenharmony_ci struct netlink_policy_dump_state *state; 137362306a36Sopenharmony_ci const struct genl_family *rt; 137462306a36Sopenharmony_ci struct genl_op_iter *op_iter; 137562306a36Sopenharmony_ci u32 op; 137662306a36Sopenharmony_ci u16 fam_id; 137762306a36Sopenharmony_ci u8 dump_map:1, 137862306a36Sopenharmony_ci single_op:1; 137962306a36Sopenharmony_ci}; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic const struct nla_policy ctrl_policy_policy[] = { 138262306a36Sopenharmony_ci [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, 138362306a36Sopenharmony_ci [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, 138462306a36Sopenharmony_ci .len = GENL_NAMSIZ - 1 }, 138562306a36Sopenharmony_ci [CTRL_ATTR_OP] = { .type = NLA_U32 }, 138662306a36Sopenharmony_ci}; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic int ctrl_dumppolicy_start(struct netlink_callback *cb) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci const struct genl_dumpit_info *info = genl_dumpit_info(cb); 139162306a36Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 139262306a36Sopenharmony_ci struct nlattr **tb = info->info.attrs; 139362306a36Sopenharmony_ci const struct genl_family *rt; 139462306a36Sopenharmony_ci struct genl_op_iter i; 139562306a36Sopenharmony_ci int err; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME]) 140062306a36Sopenharmony_ci return -EINVAL; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (tb[CTRL_ATTR_FAMILY_ID]) { 140362306a36Sopenharmony_ci ctx->fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); 140462306a36Sopenharmony_ci } else { 140562306a36Sopenharmony_ci rt = genl_family_find_byname( 140662306a36Sopenharmony_ci nla_data(tb[CTRL_ATTR_FAMILY_NAME])); 140762306a36Sopenharmony_ci if (!rt) 140862306a36Sopenharmony_ci return -ENOENT; 140962306a36Sopenharmony_ci ctx->fam_id = rt->id; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci rt = genl_family_find_byid(ctx->fam_id); 141362306a36Sopenharmony_ci if (!rt) 141462306a36Sopenharmony_ci return -ENOENT; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci ctx->rt = rt; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (tb[CTRL_ATTR_OP]) { 141962306a36Sopenharmony_ci struct genl_split_ops doit, dump; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci ctx->single_op = true; 142262306a36Sopenharmony_ci ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci err = genl_get_cmd_both(ctx->op, rt, &doit, &dump); 142562306a36Sopenharmony_ci if (err) { 142662306a36Sopenharmony_ci NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); 142762306a36Sopenharmony_ci return err; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (doit.policy) { 143162306a36Sopenharmony_ci err = netlink_policy_dump_add_policy(&ctx->state, 143262306a36Sopenharmony_ci doit.policy, 143362306a36Sopenharmony_ci doit.maxattr); 143462306a36Sopenharmony_ci if (err) 143562306a36Sopenharmony_ci goto err_free_state; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci if (dump.policy) { 143862306a36Sopenharmony_ci err = netlink_policy_dump_add_policy(&ctx->state, 143962306a36Sopenharmony_ci dump.policy, 144062306a36Sopenharmony_ci dump.maxattr); 144162306a36Sopenharmony_ci if (err) 144262306a36Sopenharmony_ci goto err_free_state; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (!ctx->state) 144662306a36Sopenharmony_ci return -ENODATA; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci ctx->dump_map = 1; 144962306a36Sopenharmony_ci return 0; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci ctx->op_iter = kmalloc(sizeof(*ctx->op_iter), GFP_KERNEL); 145362306a36Sopenharmony_ci if (!ctx->op_iter) 145462306a36Sopenharmony_ci return -ENOMEM; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci genl_op_iter_init(rt, ctx->op_iter); 145762306a36Sopenharmony_ci ctx->dump_map = genl_op_iter_next(ctx->op_iter); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci for (genl_op_iter_init(rt, &i); genl_op_iter_next(&i); ) { 146062306a36Sopenharmony_ci if (i.doit.policy) { 146162306a36Sopenharmony_ci err = netlink_policy_dump_add_policy(&ctx->state, 146262306a36Sopenharmony_ci i.doit.policy, 146362306a36Sopenharmony_ci i.doit.maxattr); 146462306a36Sopenharmony_ci if (err) 146562306a36Sopenharmony_ci goto err_free_state; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci if (i.dumpit.policy) { 146862306a36Sopenharmony_ci err = netlink_policy_dump_add_policy(&ctx->state, 146962306a36Sopenharmony_ci i.dumpit.policy, 147062306a36Sopenharmony_ci i.dumpit.maxattr); 147162306a36Sopenharmony_ci if (err) 147262306a36Sopenharmony_ci goto err_free_state; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (!ctx->state) { 147762306a36Sopenharmony_ci err = -ENODATA; 147862306a36Sopenharmony_ci goto err_free_op_iter; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci return 0; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cierr_free_state: 148362306a36Sopenharmony_ci netlink_policy_dump_free(ctx->state); 148462306a36Sopenharmony_cierr_free_op_iter: 148562306a36Sopenharmony_ci kfree(ctx->op_iter); 148662306a36Sopenharmony_ci return err; 148762306a36Sopenharmony_ci} 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_cistatic void *ctrl_dumppolicy_prep(struct sk_buff *skb, 149062306a36Sopenharmony_ci struct netlink_callback *cb) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 149362306a36Sopenharmony_ci void *hdr; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, 149662306a36Sopenharmony_ci cb->nlh->nlmsg_seq, &genl_ctrl, 149762306a36Sopenharmony_ci NLM_F_MULTI, CTRL_CMD_GETPOLICY); 149862306a36Sopenharmony_ci if (!hdr) 149962306a36Sopenharmony_ci return NULL; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id)) 150262306a36Sopenharmony_ci return NULL; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci return hdr; 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic int ctrl_dumppolicy_put_op(struct sk_buff *skb, 150862306a36Sopenharmony_ci struct netlink_callback *cb, 150962306a36Sopenharmony_ci struct genl_split_ops *doit, 151062306a36Sopenharmony_ci struct genl_split_ops *dumpit) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 151362306a36Sopenharmony_ci struct nlattr *nest_pol, *nest_op; 151462306a36Sopenharmony_ci void *hdr; 151562306a36Sopenharmony_ci int idx; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci /* skip if we have nothing to show */ 151862306a36Sopenharmony_ci if (!doit->policy && !dumpit->policy) 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci hdr = ctrl_dumppolicy_prep(skb, cb); 152262306a36Sopenharmony_ci if (!hdr) 152362306a36Sopenharmony_ci return -ENOBUFS; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci nest_pol = nla_nest_start(skb, CTRL_ATTR_OP_POLICY); 152662306a36Sopenharmony_ci if (!nest_pol) 152762306a36Sopenharmony_ci goto err; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci nest_op = nla_nest_start(skb, doit->cmd); 153062306a36Sopenharmony_ci if (!nest_op) 153162306a36Sopenharmony_ci goto err; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (doit->policy) { 153462306a36Sopenharmony_ci idx = netlink_policy_dump_get_policy_idx(ctx->state, 153562306a36Sopenharmony_ci doit->policy, 153662306a36Sopenharmony_ci doit->maxattr); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) 153962306a36Sopenharmony_ci goto err; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci if (dumpit->policy) { 154262306a36Sopenharmony_ci idx = netlink_policy_dump_get_policy_idx(ctx->state, 154362306a36Sopenharmony_ci dumpit->policy, 154462306a36Sopenharmony_ci dumpit->maxattr); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) 154762306a36Sopenharmony_ci goto err; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci nla_nest_end(skb, nest_op); 155162306a36Sopenharmony_ci nla_nest_end(skb, nest_pol); 155262306a36Sopenharmony_ci genlmsg_end(skb, hdr); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci return 0; 155562306a36Sopenharmony_cierr: 155662306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 155762306a36Sopenharmony_ci return -ENOBUFS; 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 156362306a36Sopenharmony_ci void *hdr; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (ctx->dump_map) { 156662306a36Sopenharmony_ci if (ctx->single_op) { 156762306a36Sopenharmony_ci struct genl_split_ops doit, dumpit; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (WARN_ON(genl_get_cmd_both(ctx->op, ctx->rt, 157062306a36Sopenharmony_ci &doit, &dumpit))) 157162306a36Sopenharmony_ci return -ENOENT; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (ctrl_dumppolicy_put_op(skb, cb, &doit, &dumpit)) 157462306a36Sopenharmony_ci return skb->len; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* done with the per-op policy index list */ 157762306a36Sopenharmony_ci ctx->dump_map = 0; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci while (ctx->dump_map) { 158162306a36Sopenharmony_ci if (ctrl_dumppolicy_put_op(skb, cb, 158262306a36Sopenharmony_ci &ctx->op_iter->doit, 158362306a36Sopenharmony_ci &ctx->op_iter->dumpit)) 158462306a36Sopenharmony_ci return skb->len; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci ctx->dump_map = genl_op_iter_next(ctx->op_iter); 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci while (netlink_policy_dump_loop(ctx->state)) { 159162306a36Sopenharmony_ci struct nlattr *nest; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci hdr = ctrl_dumppolicy_prep(skb, cb); 159462306a36Sopenharmony_ci if (!hdr) 159562306a36Sopenharmony_ci goto nla_put_failure; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci nest = nla_nest_start(skb, CTRL_ATTR_POLICY); 159862306a36Sopenharmony_ci if (!nest) 159962306a36Sopenharmony_ci goto nla_put_failure; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (netlink_policy_dump_write(skb, ctx->state)) 160262306a36Sopenharmony_ci goto nla_put_failure; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci nla_nest_end(skb, nest); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci genlmsg_end(skb, hdr); 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return skb->len; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cinla_put_failure: 161262306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 161362306a36Sopenharmony_ci return skb->len; 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic int ctrl_dumppolicy_done(struct netlink_callback *cb) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci kfree(ctx->op_iter); 162162306a36Sopenharmony_ci netlink_policy_dump_free(ctx->state); 162262306a36Sopenharmony_ci return 0; 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic const struct genl_split_ops genl_ctrl_ops[] = { 162662306a36Sopenharmony_ci { 162762306a36Sopenharmony_ci .cmd = CTRL_CMD_GETFAMILY, 162862306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT, 162962306a36Sopenharmony_ci .policy = ctrl_policy_family, 163062306a36Sopenharmony_ci .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, 163162306a36Sopenharmony_ci .doit = ctrl_getfamily, 163262306a36Sopenharmony_ci .flags = GENL_CMD_CAP_DO, 163362306a36Sopenharmony_ci }, 163462306a36Sopenharmony_ci { 163562306a36Sopenharmony_ci .cmd = CTRL_CMD_GETFAMILY, 163662306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_DUMP, 163762306a36Sopenharmony_ci .policy = ctrl_policy_family, 163862306a36Sopenharmony_ci .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, 163962306a36Sopenharmony_ci .dumpit = ctrl_dumpfamily, 164062306a36Sopenharmony_ci .flags = GENL_CMD_CAP_DUMP, 164162306a36Sopenharmony_ci }, 164262306a36Sopenharmony_ci { 164362306a36Sopenharmony_ci .cmd = CTRL_CMD_GETPOLICY, 164462306a36Sopenharmony_ci .policy = ctrl_policy_policy, 164562306a36Sopenharmony_ci .maxattr = ARRAY_SIZE(ctrl_policy_policy) - 1, 164662306a36Sopenharmony_ci .start = ctrl_dumppolicy_start, 164762306a36Sopenharmony_ci .dumpit = ctrl_dumppolicy, 164862306a36Sopenharmony_ci .done = ctrl_dumppolicy_done, 164962306a36Sopenharmony_ci .flags = GENL_CMD_CAP_DUMP, 165062306a36Sopenharmony_ci }, 165162306a36Sopenharmony_ci}; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic const struct genl_multicast_group genl_ctrl_groups[] = { 165462306a36Sopenharmony_ci { .name = "notify", }, 165562306a36Sopenharmony_ci}; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic struct genl_family genl_ctrl __ro_after_init = { 165862306a36Sopenharmony_ci .module = THIS_MODULE, 165962306a36Sopenharmony_ci .split_ops = genl_ctrl_ops, 166062306a36Sopenharmony_ci .n_split_ops = ARRAY_SIZE(genl_ctrl_ops), 166162306a36Sopenharmony_ci .resv_start_op = CTRL_CMD_GETPOLICY + 1, 166262306a36Sopenharmony_ci .mcgrps = genl_ctrl_groups, 166362306a36Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(genl_ctrl_groups), 166462306a36Sopenharmony_ci .id = GENL_ID_CTRL, 166562306a36Sopenharmony_ci .name = "nlctrl", 166662306a36Sopenharmony_ci .version = 0x2, 166762306a36Sopenharmony_ci .netnsok = true, 166862306a36Sopenharmony_ci}; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic int genl_bind(struct net *net, int group) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci const struct genl_family *family; 167362306a36Sopenharmony_ci unsigned int id; 167462306a36Sopenharmony_ci int ret = 0; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci down_read(&cb_lock); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, family, id) { 167962306a36Sopenharmony_ci const struct genl_multicast_group *grp; 168062306a36Sopenharmony_ci int i; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci if (family->n_mcgrps == 0) 168362306a36Sopenharmony_ci continue; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci i = group - family->mcgrp_offset; 168662306a36Sopenharmony_ci if (i < 0 || i >= family->n_mcgrps) 168762306a36Sopenharmony_ci continue; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci grp = &family->mcgrps[i]; 169062306a36Sopenharmony_ci if ((grp->flags & GENL_UNS_ADMIN_PERM) && 169162306a36Sopenharmony_ci !ns_capable(net->user_ns, CAP_NET_ADMIN)) 169262306a36Sopenharmony_ci ret = -EPERM; 169362306a36Sopenharmony_ci if (grp->cap_sys_admin && 169462306a36Sopenharmony_ci !ns_capable(net->user_ns, CAP_SYS_ADMIN)) 169562306a36Sopenharmony_ci ret = -EPERM; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci break; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci up_read(&cb_lock); 170162306a36Sopenharmony_ci return ret; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic int __net_init genl_pernet_init(struct net *net) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci struct netlink_kernel_cfg cfg = { 170762306a36Sopenharmony_ci .input = genl_rcv, 170862306a36Sopenharmony_ci .flags = NL_CFG_F_NONROOT_RECV, 170962306a36Sopenharmony_ci .bind = genl_bind, 171062306a36Sopenharmony_ci }; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* we'll bump the group number right afterwards */ 171362306a36Sopenharmony_ci net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (!net->genl_sock && net_eq(net, &init_net)) 171662306a36Sopenharmony_ci panic("GENL: Cannot initialize generic netlink\n"); 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!net->genl_sock) 171962306a36Sopenharmony_ci return -ENOMEM; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return 0; 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic void __net_exit genl_pernet_exit(struct net *net) 172562306a36Sopenharmony_ci{ 172662306a36Sopenharmony_ci netlink_kernel_release(net->genl_sock); 172762306a36Sopenharmony_ci net->genl_sock = NULL; 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic struct pernet_operations genl_pernet_ops = { 173162306a36Sopenharmony_ci .init = genl_pernet_init, 173262306a36Sopenharmony_ci .exit = genl_pernet_exit, 173362306a36Sopenharmony_ci}; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic int __init genl_init(void) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci int err; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci err = genl_register_family(&genl_ctrl); 174062306a36Sopenharmony_ci if (err < 0) 174162306a36Sopenharmony_ci goto problem; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci err = register_pernet_subsys(&genl_pernet_ops); 174462306a36Sopenharmony_ci if (err) 174562306a36Sopenharmony_ci goto problem; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci return 0; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ciproblem: 175062306a36Sopenharmony_ci panic("GENL: Cannot register controller: %d\n", err); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cicore_initcall(genl_init); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, 175662306a36Sopenharmony_ci gfp_t flags) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct sk_buff *tmp; 175962306a36Sopenharmony_ci struct net *net, *prev = NULL; 176062306a36Sopenharmony_ci bool delivered = false; 176162306a36Sopenharmony_ci int err; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci for_each_net_rcu(net) { 176462306a36Sopenharmony_ci if (prev) { 176562306a36Sopenharmony_ci tmp = skb_clone(skb, flags); 176662306a36Sopenharmony_ci if (!tmp) { 176762306a36Sopenharmony_ci err = -ENOMEM; 176862306a36Sopenharmony_ci goto error; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci err = nlmsg_multicast(prev->genl_sock, tmp, 177162306a36Sopenharmony_ci portid, group, flags); 177262306a36Sopenharmony_ci if (!err) 177362306a36Sopenharmony_ci delivered = true; 177462306a36Sopenharmony_ci else if (err != -ESRCH) 177562306a36Sopenharmony_ci goto error; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci prev = net; 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags); 178262306a36Sopenharmony_ci if (!err) 178362306a36Sopenharmony_ci delivered = true; 178462306a36Sopenharmony_ci else if (err != -ESRCH) 178562306a36Sopenharmony_ci return err; 178662306a36Sopenharmony_ci return delivered ? 0 : -ESRCH; 178762306a36Sopenharmony_ci error: 178862306a36Sopenharmony_ci kfree_skb(skb); 178962306a36Sopenharmony_ci return err; 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ciint genlmsg_multicast_allns(const struct genl_family *family, 179362306a36Sopenharmony_ci struct sk_buff *skb, u32 portid, 179462306a36Sopenharmony_ci unsigned int group, gfp_t flags) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci if (WARN_ON_ONCE(group >= family->n_mcgrps)) 179762306a36Sopenharmony_ci return -EINVAL; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci group = family->mcgrp_offset + group; 180062306a36Sopenharmony_ci return genlmsg_mcast(skb, portid, group, flags); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ciEXPORT_SYMBOL(genlmsg_multicast_allns); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_civoid genl_notify(const struct genl_family *family, struct sk_buff *skb, 180562306a36Sopenharmony_ci struct genl_info *info, u32 group, gfp_t flags) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci struct net *net = genl_info_net(info); 180862306a36Sopenharmony_ci struct sock *sk = net->genl_sock; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (WARN_ON_ONCE(group >= family->n_mcgrps)) 181162306a36Sopenharmony_ci return; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci group = family->mcgrp_offset + group; 181462306a36Sopenharmony_ci nlmsg_notify(sk, skb, info->snd_portid, group, 181562306a36Sopenharmony_ci nlmsg_report(info->nlhdr), flags); 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ciEXPORT_SYMBOL(genl_notify); 1818