📄 genetlink.c
字号:
/* * NETLINK Generic Netlink Family * * Authors: Jamal Hadi Salim * Thomas Graf <tgraf@suug.ch> * Johannes Berg <johannes@sipsolutions.net> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/mutex.h>#include <linux/bitmap.h>#include <net/sock.h>#include <net/genetlink.h>struct sock *genl_sock = NULL;static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */static inline void genl_lock(void){ mutex_lock(&genl_mutex);}static inline void genl_unlock(void){ mutex_unlock(&genl_mutex);}#define GENL_FAM_TAB_SIZE 16#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)static struct list_head family_ht[GENL_FAM_TAB_SIZE];/* * Bitmap of multicast groups that are currently in use. * * To avoid an allocation at boot of just one unsigned long, * declare it global instead. * Bit 0 is marked as already used since group 0 is invalid. */static unsigned long mc_group_start = 0x1;static unsigned long *mc_groups = &mc_group_start;static unsigned long mc_groups_longs = 1;static int genl_ctrl_event(int event, void *data);static inline unsigned int genl_family_hash(unsigned int id){ return id & GENL_FAM_TAB_MASK;}static inline struct list_head *genl_family_chain(unsigned int id){ return &family_ht[genl_family_hash(id)];}static struct genl_family *genl_family_find_byid(unsigned int id){ struct genl_family *f; list_for_each_entry(f, genl_family_chain(id), family_list) if (f->id == id) return f; return NULL;}static struct genl_family *genl_family_find_byname(char *name){ struct genl_family *f; int i; for (i = 0; i < GENL_FAM_TAB_SIZE; i++) list_for_each_entry(f, genl_family_chain(i), family_list) if (strcmp(f->name, name) == 0) return f; return NULL;}static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family){ struct genl_ops *ops; list_for_each_entry(ops, &family->ops_list, ops_list) if (ops->cmd == cmd) return ops; return NULL;}/* Of course we are going to have problems once we hit * 2^16 alive types, but that can only happen by year 2K*/static inline u16 genl_generate_id(void){ static u16 id_gen_idx; int overflowed = 0; do { if (id_gen_idx == 0) id_gen_idx = GENL_MIN_ID; if (++id_gen_idx > GENL_MAX_ID) { if (!overflowed) { overflowed = 1; id_gen_idx = 0; continue; } else return 0; } } while (genl_family_find_byid(id_gen_idx)); return id_gen_idx;}static struct genl_multicast_group notify_grp;/** * genl_register_mc_group - register a multicast group * * Registers the specified multicast group and notifies userspace * about the new group. * * Returns 0 on success or a negative error code. * * @family: The generic netlink family the group shall be registered for. * @grp: The group to register, must have a name. */int genl_register_mc_group(struct genl_family *family, struct genl_multicast_group *grp){ int id; unsigned long *new_groups; int err; BUG_ON(grp->name[0] == '\0'); genl_lock(); /* special-case our own group */ if (grp == ¬ify_grp) id = GENL_ID_CTRL; else id = find_first_zero_bit(mc_groups, mc_groups_longs * BITS_PER_LONG); if (id >= mc_groups_longs * BITS_PER_LONG) { size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long); if (mc_groups == &mc_group_start) { new_groups = kzalloc(nlen, GFP_KERNEL); if (!new_groups) { err = -ENOMEM; goto out; } mc_groups = new_groups; *mc_groups = mc_group_start; } else { new_groups = krealloc(mc_groups, nlen, GFP_KERNEL); if (!new_groups) { err = -ENOMEM; goto out; } mc_groups = new_groups; mc_groups[mc_groups_longs] = 0; } mc_groups_longs++; } err = netlink_change_ngroups(genl_sock, mc_groups_longs * BITS_PER_LONG); if (err) goto out; grp->id = id; set_bit(id, mc_groups); list_add_tail(&grp->list, &family->mcast_groups); grp->family = family; genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp); out: genl_unlock(); return err;}EXPORT_SYMBOL(genl_register_mc_group);static void __genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp){ BUG_ON(grp->family != family); netlink_clear_multicast_users(genl_sock, grp->id); clear_bit(grp->id, mc_groups); list_del(&grp->list); genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); grp->id = 0; grp->family = NULL;}/** * genl_unregister_mc_group - unregister a multicast group * * Unregisters the specified multicast group and notifies userspace * about it. All current listeners on the group are removed. * * Note: It is not necessary to unregister all multicast groups before * unregistering the family, unregistering the family will cause * all assigned multicast groups to be unregistered automatically. * * @family: Generic netlink family the group belongs to. * @grp: The group to unregister, must have been registered successfully * previously. */void genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp){ genl_lock(); __genl_unregister_mc_group(family, grp); genl_unlock();}static void genl_unregister_mc_groups(struct genl_family *family){ struct genl_multicast_group *grp, *tmp; genl_lock(); list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list) __genl_unregister_mc_group(family, grp); genl_unlock();}/** * genl_register_ops - register generic netlink operations * @family: generic netlink family * @ops: operations to be registered * * Registers the specified operations and assigns them to the specified * family. Either a doit or dumpit callback must be specified or the * operation will fail. Only one operation structure per command * identifier may be registered. * * See include/net/genetlink.h for more documenation on the operations * structure. * * Returns 0 on success or a negative error code. */int genl_register_ops(struct genl_family *family, struct genl_ops *ops){ int err = -EINVAL; if (ops->dumpit == NULL && ops->doit == NULL) goto errout; if (genl_get_cmd(ops->cmd, family)) { err = -EEXIST; goto errout; } if (ops->dumpit) ops->flags |= GENL_CMD_CAP_DUMP; if (ops->doit) ops->flags |= GENL_CMD_CAP_DO; if (ops->policy) ops->flags |= GENL_CMD_CAP_HASPOL; genl_lock(); list_add_tail(&ops->ops_list, &family->ops_list); genl_unlock(); genl_ctrl_event(CTRL_CMD_NEWOPS, ops); err = 0;errout: return err;}/** * genl_unregister_ops - unregister generic netlink operations * @family: generic netlink family * @ops: operations to be unregistered * * Unregisters the specified operations and unassigns them from the * specified family. The operation blocks until the current message * processing has finished and doesn't start again until the * unregister process has finished. * * Note: It is not necessary to unregister all operations before * unregistering the family, unregistering the family will cause * all assigned operations to be unregistered automatically. * * Returns 0 on success or a negative error code. */int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops){ struct genl_ops *rc; genl_lock(); list_for_each_entry(rc, &family->ops_list, ops_list) { if (rc == ops) { list_del(&ops->ops_list); genl_unlock(); genl_ctrl_event(CTRL_CMD_DELOPS, ops); return 0; } } genl_unlock(); return -ENOENT;}/** * genl_register_family - register a generic netlink family * @family: generic netlink family * * Registers the specified family after validating it first. Only one * family may be registered with the same family name or identifier. * The family id may equal GENL_ID_GENERATE causing an unique id to * be automatically generated and assigned. * * Return 0 on success or a negative error code. */int genl_register_family(struct genl_family *family){ int err = -EINVAL; if (family->id && family->id < GENL_MIN_ID) goto errout; if (family->id > GENL_MAX_ID) goto errout; INIT_LIST_HEAD(&family->ops_list); INIT_LIST_HEAD(&family->mcast_groups); genl_lock(); if (genl_family_find_byname(family->name)) { err = -EEXIST; goto errout_locked; } if (genl_family_find_byid(family->id)) { err = -EEXIST; goto errout_locked; } if (family->id == GENL_ID_GENERATE) { u16 newid = genl_generate_id(); if (!newid) { err = -ENOMEM; goto errout_locked; } family->id = newid; } if (family->maxattr) { family->attrbuf = kmalloc((family->maxattr+1) * sizeof(struct nlattr *), GFP_KERNEL); if (family->attrbuf == NULL) { err = -ENOMEM; goto errout_locked; } } else family->attrbuf = NULL; list_add_tail(&family->family_list, genl_family_chain(family->id)); genl_unlock(); genl_ctrl_event(CTRL_CMD_NEWFAMILY, family); return 0;errout_locked: genl_unlock();errout: return err;}/** * genl_unregister_family - unregister generic netlink family * @family: generic netlink family * * Unregisters the specified family. * * Returns 0 on success or a negative error code. */int genl_unregister_family(struct genl_family *family){ struct genl_family *rc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -