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