xref: /freebsd/sys/netlink/netlink_generic_kpi.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/ck.h>
30 #include <sys/epoch.h>
31 #include <sys/eventhandler.h>
32 #include <sys/kernel.h>
33 #include <sys/lock.h>
34 #include <sys/malloc.h>
35 #include <sys/socket.h>
36 #include <sys/sx.h>
37 
38 #include <netlink/netlink.h>
39 #include <netlink/netlink_ctl.h>
40 #include <netlink/netlink_generic.h>
41 #include <netlink/netlink_var.h>
42 
43 #define	DEBUG_MOD_NAME	nl_generic_kpi
44 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
45 #include <netlink/netlink_debug.h>
46 _DECLARE_DEBUG(LOG_INFO);
47 
48 
49 /*
50  * NETLINK_GENERIC families/groups registration logic
51  */
52 
53 #define	GENL_LOCK()		sx_xlock(&sx_lock)
54 #define	GENL_UNLOCK()		sx_xunlock(&sx_lock)
55 static struct sx sx_lock;
56 SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock");
57 
58 static struct genl_family	families[MAX_FAMILIES];
59 static struct genl_group	groups[MAX_GROUPS];
60 
61 static struct genl_family *
62 find_family(const char *family_name)
63 {
64 	for (int i = 0; i < MAX_FAMILIES; i++) {
65 		struct genl_family *gf = &families[i];
66 		if (gf->family_name != NULL && !strcmp(gf->family_name, family_name))
67 			return (gf);
68 	}
69 
70 	return (NULL);
71 }
72 
73 static struct genl_family *
74 find_empty_family_id(const char *family_name)
75 {
76 	struct genl_family *gf = NULL;
77 
78 	if (!strcmp(family_name, CTRL_FAMILY_NAME)) {
79 		gf = &families[0];
80 		gf->family_id = GENL_MIN_ID;
81 	} else {
82 		/* Index 0 is reserved for the control family */
83 		for (int i = 1; i < MAX_FAMILIES; i++) {
84 			gf = &families[i];
85 			if (gf->family_name == NULL) {
86 				gf->family_id = GENL_MIN_ID + i;
87 				break;
88 			}
89 		}
90 	}
91 
92 	return (gf);
93 }
94 
95 uint32_t
96 genl_register_family(const char *family_name, size_t hdrsize, int family_version,
97     int max_attr_idx)
98 {
99 	uint32_t family_id = 0;
100 
101 	MPASS(family_name != NULL);
102 	if (find_family(family_name) != NULL)
103 		return (0);
104 
105 	GENL_LOCK();
106 
107 	struct genl_family *gf = find_empty_family_id(family_name);
108 	MPASS(gf != NULL);
109 
110 	gf->family_name = family_name;
111 	gf->family_version = family_version;
112 	gf->family_hdrsize = hdrsize;
113 	gf->family_attr_max = max_attr_idx;
114 	NL_LOG(LOG_DEBUG2, "Registered family %s id %d", gf->family_name, gf->family_id);
115 	family_id = gf->family_id;
116 	EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY);
117 
118 	GENL_UNLOCK();
119 
120 	return (family_id);
121 }
122 
123 static void
124 free_family(struct genl_family *gf)
125 {
126 	if (gf->family_cmds != NULL)
127 		free(gf->family_cmds, M_NETLINK);
128 }
129 
130 /*
131  * unregister groups of a given family
132  */
133 static void
134 unregister_groups(const struct genl_family *gf)
135 {
136 
137 	for (int i = 0; i < MAX_GROUPS; i++) {
138 		struct genl_group *gg = &groups[i];
139 		if (gg->group_family == gf && gg->group_name != NULL) {
140 			gg->group_family = NULL;
141 			gg->group_name = NULL;
142 		}
143 	}
144 }
145 
146 /*
147  * Can sleep, I guess
148  */
149 bool
150 genl_unregister_family(const char *family_name)
151 {
152 	bool found = false;
153 
154 	GENL_LOCK();
155 	struct genl_family *gf = find_family(family_name);
156 
157 	if (gf != NULL) {
158 		EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY);
159 		found = true;
160 		unregister_groups(gf);
161 		/* TODO: zero pointer first */
162 		free_family(gf);
163 		bzero(gf, sizeof(*gf));
164 	}
165 	GENL_UNLOCK();
166 
167 	return (found);
168 }
169 
170 bool
171 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count)
172 {
173 	GENL_LOCK();
174 	struct genl_family *gf = find_family(family_name);
175 	if (gf == NULL) {
176 		GENL_UNLOCK();
177 		return (false);
178 	}
179 
180 	int cmd_size = gf->family_cmd_size;
181 
182 	for (int i = 0; i < count; i++) {
183 		MPASS(cmds[i].cmd_cb != NULL);
184 		if (cmds[i].cmd_num >= cmd_size)
185 			cmd_size = cmds[i].cmd_num + 1;
186 	}
187 
188 	if (cmd_size > gf->family_cmd_size) {
189 		/* need to realloc */
190 		size_t sz = cmd_size * sizeof(struct genl_cmd);
191 		void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO);
192 
193 		memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd));
194 		void *old_data = gf->family_cmds;
195 		gf->family_cmds = data;
196 		gf->family_cmd_size = cmd_size;
197 		free(old_data, M_NETLINK);
198 	}
199 
200 	for (int i = 0; i < count; i++) {
201 		const struct genl_cmd *cmd = &cmds[i];
202 		MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL);
203 		gf->family_cmds[cmd->cmd_num] = cmds[i];
204 		NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s",
205 		    cmd->cmd_name, cmd->cmd_num, gf->family_name);
206 	}
207 	GENL_UNLOCK();
208 	return (true);
209 }
210 
211 static struct genl_group *
212 find_group(const struct genl_family *gf, const char *group_name)
213 {
214 	for (int i = 0; i < MAX_GROUPS; i++) {
215 		struct genl_group *gg = &groups[i];
216 		if (gg->group_family == gf && !strcmp(gg->group_name, group_name))
217 			return (gg);
218 	}
219 	return (NULL);
220 }
221 
222 uint32_t
223 genl_register_group(const char *family_name, const char *group_name)
224 {
225 	uint32_t group_id = 0;
226 
227 	MPASS(family_name != NULL);
228 	MPASS(group_name != NULL);
229 
230 	GENL_LOCK();
231 	struct genl_family *gf = find_family(family_name);
232 
233 	if (gf == NULL || find_group(gf, group_name) != NULL) {
234 		GENL_UNLOCK();
235 		return (0);
236 	}
237 
238 	for (int i = 0; i < MAX_GROUPS; i++) {
239 		struct genl_group *gg = &groups[i];
240 		if (gg->group_family == NULL) {
241 			gf->family_num_groups++;
242 			gg->group_family = gf;
243 			gg->group_name = group_name;
244 			group_id = i + MIN_GROUP_NUM;
245 			break;
246 		}
247 	}
248 	GENL_UNLOCK();
249 
250 	return (group_id);
251 }
252 
253 /* accessors */
254 struct genl_family *
255 genl_get_family(uint32_t family_id)
256 {
257 	return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL);
258 }
259 
260 const char *
261 genl_get_family_name(const struct genl_family *gf)
262 {
263 	return (gf->family_name);
264 }
265 
266 uint32_t
267 genl_get_family_id(const struct genl_family *gf)
268 {
269 	return (gf->family_id);
270 }
271 
272 struct genl_group *
273 genl_get_group(uint32_t group_id)
274 {
275 	return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL);
276 }
277 
278