2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 #include <sys/types.h>
31 #include <sys/epoch.h>
32 #include <sys/eventhandler.h>
33 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/socket.h>
39 #include <netlink/netlink.h>
40 #include <netlink/netlink_ctl.h>
41 #include <netlink/netlink_generic.h>
42 #include <netlink/netlink_var.h>
44 #define DEBUG_MOD_NAME nl_generic_kpi
45 #define DEBUG_MAX_LEVEL LOG_DEBUG3
46 #include <netlink/netlink_debug.h>
47 _DECLARE_DEBUG(LOG_INFO);
51 * NETLINK_GENERIC families/groups registration logic
54 #define GENL_LOCK() sx_xlock(&sx_lock)
55 #define GENL_UNLOCK() sx_xunlock(&sx_lock)
56 static struct sx sx_lock;
57 SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock");
59 static struct genl_family families[MAX_FAMILIES];
60 static struct genl_group groups[MAX_GROUPS];
62 static struct genl_family *
63 find_family(const char *family_name)
65 for (int i = 0; i < MAX_FAMILIES; i++) {
66 struct genl_family *gf = &families[i];
67 if (gf->family_name != NULL && !strcmp(gf->family_name, family_name))
74 static struct genl_family *
75 find_empty_family_id(const char *family_name)
77 struct genl_family *gf = NULL;
79 if (!strcmp(family_name, CTRL_FAMILY_NAME)) {
81 gf->family_id = GENL_MIN_ID;
83 /* Index 0 is reserved for the control family */
84 for (int i = 1; i < MAX_FAMILIES; i++) {
86 if (gf->family_name == NULL) {
87 gf->family_id = GENL_MIN_ID + i;
97 genl_register_family(const char *family_name, size_t hdrsize, int family_version,
100 uint32_t family_id = 0;
102 MPASS(family_name != NULL);
103 if (find_family(family_name) != NULL)
108 struct genl_family *gf = find_empty_family_id(family_name);
111 gf->family_name = family_name;
112 gf->family_version = family_version;
113 gf->family_hdrsize = hdrsize;
114 gf->family_attr_max = max_attr_idx;
115 NL_LOG(LOG_DEBUG2, "Registered family %s id %d", gf->family_name, gf->family_id);
116 family_id = gf->family_id;
117 EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY);
125 free_family(struct genl_family *gf)
127 if (gf->family_cmds != NULL)
128 free(gf->family_cmds, M_NETLINK);
132 * unregister groups of a given family
135 unregister_groups(const struct genl_family *gf)
138 for (int i = 0; i < MAX_GROUPS; i++) {
139 struct genl_group *gg = &groups[i];
140 if (gg->group_family == gf && gg->group_name != NULL) {
141 gg->group_family = NULL;
142 gg->group_name = NULL;
151 genl_unregister_family(const char *family_name)
156 struct genl_family *gf = find_family(family_name);
159 EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY);
161 unregister_groups(gf);
162 /* TODO: zero pointer first */
164 bzero(gf, sizeof(*gf));
172 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count)
175 struct genl_family *gf = find_family(family_name);
181 int cmd_size = gf->family_cmd_size;
183 for (int i = 0; i < count; i++) {
184 MPASS(cmds[i].cmd_cb != NULL);
185 if (cmds[i].cmd_num >= cmd_size)
186 cmd_size = cmds[i].cmd_num + 1;
189 if (cmd_size > gf->family_cmd_size) {
190 /* need to realloc */
191 size_t sz = cmd_size * sizeof(struct genl_cmd);
192 void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO);
194 memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd));
195 void *old_data = gf->family_cmds;
196 gf->family_cmds = data;
197 gf->family_cmd_size = cmd_size;
198 free(old_data, M_NETLINK);
201 for (int i = 0; i < count; i++) {
202 const struct genl_cmd *cmd = &cmds[i];
203 MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL);
204 gf->family_cmds[cmd->cmd_num] = cmds[i];
205 NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s",
206 cmd->cmd_name, cmd->cmd_num, gf->family_name);
212 static struct genl_group *
213 find_group(const struct genl_family *gf, const char *group_name)
215 for (int i = 0; i < MAX_GROUPS; i++) {
216 struct genl_group *gg = &groups[i];
217 if (gg->group_family == gf && !strcmp(gg->group_name, group_name))
224 genl_register_group(const char *family_name, const char *group_name)
226 uint32_t group_id = 0;
228 MPASS(family_name != NULL);
229 MPASS(group_name != NULL);
232 struct genl_family *gf = find_family(family_name);
234 if (gf == NULL || find_group(gf, group_name) != NULL) {
239 for (int i = 0; i < MAX_GROUPS; i++) {
240 struct genl_group *gg = &groups[i];
241 if (gg->group_family == NULL) {
242 gf->family_num_groups++;
243 gg->group_family = gf;
244 gg->group_name = group_name;
245 group_id = i + MIN_GROUP_NUM;
256 genl_get_family(uint32_t family_id)
258 return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL);
262 genl_get_family_name(const struct genl_family *gf)
264 return (gf->family_name);
268 genl_get_family_id(const struct genl_family *gf)
270 return (gf->family_id);
274 genl_get_group(uint32_t group_id)
276 return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL);