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)
55static struct sx sx_lock;
56SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock");
57
58static struct genl_family	families[MAX_FAMILIES];
59static struct genl_group	groups[MAX_GROUPS];
60
61static struct genl_family *
62find_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
73static struct genl_family *
74find_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
95uint32_t
96genl_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
123static void
124free_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 */
133static void
134unregister_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 */
149bool
150genl_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
170bool
171genl_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
211static struct genl_group *
212find_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
222uint32_t
223genl_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 */
254struct genl_family *
255genl_get_family(uint32_t family_id)
256{
257	return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL);
258}
259
260const char *
261genl_get_family_name(const struct genl_family *gf)
262{
263	return (gf->family_name);
264}
265
266uint32_t
267genl_get_family_id(const struct genl_family *gf)
268{
269	return (gf->family_id);
270}
271
272struct genl_group *
273genl_get_group(uint32_t group_id)
274{
275	return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL);
276}
277
278