xref: /freebsd/sys/netlink/netlink_generic_kpi.c (revision b1879975794772ee51f0b4865753364c7d7626c3)
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,
97     uint16_t family_version, uint16_t max_attr_idx)
98 {
99 
100 	MPASS(family_name != NULL);
101 	if (find_family(family_name) != NULL)
102 		return (0);
103 
104 	GENL_LOCK();
105 
106 	struct genl_family *gf = find_empty_family_id(family_name);
107 	MPASS(gf != NULL);
108 
109 	gf->family_name = family_name;
110 	gf->family_version = family_version;
111 	gf->family_hdrsize = hdrsize;
112 	gf->family_attr_max = max_attr_idx;
113 	NL_LOG(LOG_DEBUG2, "Registered family %s id %d", gf->family_name,
114 	    gf->family_id);
115 	EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY);
116 
117 	GENL_UNLOCK();
118 
119 	return (gf->family_id);
120 }
121 
122 static void
123 free_family(struct genl_family *gf)
124 {
125 	if (gf->family_cmds != NULL)
126 		free(gf->family_cmds, M_NETLINK);
127 }
128 
129 /*
130  * unregister groups of a given family
131  */
132 static void
133 unregister_groups(const struct genl_family *gf)
134 {
135 
136 	for (int i = 0; i < MAX_GROUPS; i++) {
137 		struct genl_group *gg = &groups[i];
138 		if (gg->group_family == gf && gg->group_name != NULL) {
139 			gg->group_family = NULL;
140 			gg->group_name = NULL;
141 		}
142 	}
143 }
144 
145 /*
146  * Can sleep, I guess
147  */
148 bool
149 genl_unregister_family(const char *family_name)
150 {
151 	bool found = false;
152 
153 	GENL_LOCK();
154 	struct genl_family *gf = find_family(family_name);
155 
156 	if (gf != NULL) {
157 		EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY);
158 		found = true;
159 		unregister_groups(gf);
160 		/* TODO: zero pointer first */
161 		free_family(gf);
162 		bzero(gf, sizeof(*gf));
163 	}
164 	GENL_UNLOCK();
165 
166 	return (found);
167 }
168 
169 bool
170 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count)
171 {
172 	GENL_LOCK();
173 	struct genl_family *gf = find_family(family_name);
174 	if (gf == NULL) {
175 		GENL_UNLOCK();
176 		return (false);
177 	}
178 
179 	int cmd_size = gf->family_cmd_size;
180 
181 	for (int i = 0; i < count; i++) {
182 		MPASS(cmds[i].cmd_cb != NULL);
183 		if (cmds[i].cmd_num >= cmd_size)
184 			cmd_size = cmds[i].cmd_num + 1;
185 	}
186 
187 	if (cmd_size > gf->family_cmd_size) {
188 		/* need to realloc */
189 		size_t sz = cmd_size * sizeof(struct genl_cmd);
190 		void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO);
191 
192 		memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd));
193 		void *old_data = gf->family_cmds;
194 		gf->family_cmds = data;
195 		gf->family_cmd_size = cmd_size;
196 		free(old_data, M_NETLINK);
197 	}
198 
199 	for (int i = 0; i < count; i++) {
200 		const struct genl_cmd *cmd = &cmds[i];
201 		MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL);
202 		gf->family_cmds[cmd->cmd_num] = cmds[i];
203 		NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s",
204 		    cmd->cmd_name, cmd->cmd_num, gf->family_name);
205 	}
206 	GENL_UNLOCK();
207 	return (true);
208 }
209 
210 static struct genl_group *
211 find_group(const struct genl_family *gf, const char *group_name)
212 {
213 	for (int i = 0; i < MAX_GROUPS; i++) {
214 		struct genl_group *gg = &groups[i];
215 		if (gg->group_family == gf && !strcmp(gg->group_name, group_name))
216 			return (gg);
217 	}
218 	return (NULL);
219 }
220 
221 uint32_t
222 genl_register_group(const char *family_name, const char *group_name)
223 {
224 	uint32_t group_id = 0;
225 
226 	MPASS(family_name != NULL);
227 	MPASS(group_name != NULL);
228 
229 	GENL_LOCK();
230 	struct genl_family *gf = find_family(family_name);
231 
232 	if (gf == NULL || find_group(gf, group_name) != NULL) {
233 		GENL_UNLOCK();
234 		return (0);
235 	}
236 
237 	for (int i = 0; i < MAX_GROUPS; i++) {
238 		struct genl_group *gg = &groups[i];
239 		if (gg->group_family == NULL) {
240 			gf->family_num_groups++;
241 			gg->group_family = gf;
242 			gg->group_name = group_name;
243 			group_id = i + MIN_GROUP_NUM;
244 			break;
245 		}
246 	}
247 	GENL_UNLOCK();
248 
249 	return (group_id);
250 }
251 
252 /* accessors */
253 struct genl_family *
254 genl_get_family(uint16_t family_id)
255 {
256 	return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL);
257 }
258 
259 const char *
260 genl_get_family_name(const struct genl_family *gf)
261 {
262 	return (gf->family_name);
263 }
264 
265 uint16_t
266 genl_get_family_id(const struct genl_family *gf)
267 {
268 	return (gf->family_id);
269 }
270 
271 struct genl_group *
272 genl_get_group(uint32_t group_id)
273 {
274 	return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL);
275 }
276 
277