]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netlink/netlink_generic_kpi.c
libarchive: merge security fix from vendor branch
[FreeBSD/FreeBSD.git] / sys / netlink / netlink_generic_kpi.c
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 #include <sys/types.h>
30 #include <sys/ck.h>
31 #include <sys/epoch.h>
32 #include <sys/eventhandler.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/socket.h>
37 #include <sys/sx.h>
38
39 #include <netlink/netlink.h>
40 #include <netlink/netlink_ctl.h>
41 #include <netlink/netlink_generic.h>
42 #include <netlink/netlink_var.h>
43
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);
48
49
50 /*
51  * NETLINK_GENERIC families/groups registration logic
52  */
53
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");
58
59 static struct genl_family       families[MAX_FAMILIES];
60 static struct genl_group        groups[MAX_GROUPS];
61
62 static struct genl_family *
63 find_family(const char *family_name)
64 {
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))
68                         return (gf);
69         }
70
71         return (NULL);
72 }
73
74 static struct genl_family *
75 find_empty_family_id(const char *family_name)
76 {
77         struct genl_family *gf = NULL;
78
79         if (!strcmp(family_name, CTRL_FAMILY_NAME)) {
80                 gf = &families[0];
81                 gf->family_id = GENL_MIN_ID;
82         } else {
83                 /* Index 0 is reserved for the control family */
84                 for (int i = 1; i < MAX_FAMILIES; i++) {
85                         gf = &families[i];
86                         if (gf->family_name == NULL) {
87                                 gf->family_id = GENL_MIN_ID + i;
88                                 break;
89                         }
90                 }
91         }
92
93         return (gf);
94 }
95
96 uint32_t
97 genl_register_family(const char *family_name, size_t hdrsize, int family_version,
98     int max_attr_idx)
99 {
100         uint32_t family_id = 0;
101
102         MPASS(family_name != NULL);
103         if (find_family(family_name) != NULL)
104                 return (0);
105
106         GENL_LOCK();
107
108         struct genl_family *gf = find_empty_family_id(family_name);
109         MPASS(gf != NULL);
110
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);
118
119         GENL_UNLOCK();
120
121         return (family_id);
122 }
123
124 static void
125 free_family(struct genl_family *gf)
126 {
127         if (gf->family_cmds != NULL)
128                 free(gf->family_cmds, M_NETLINK);
129 }
130
131 /*
132  * unregister groups of a given family
133  */
134 static void
135 unregister_groups(const struct genl_family *gf)
136 {
137
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;
143                 }
144         }
145 }
146
147 /*
148  * Can sleep, I guess
149  */
150 bool
151 genl_unregister_family(const char *family_name)
152 {
153         bool found = false;
154
155         GENL_LOCK();
156         struct genl_family *gf = find_family(family_name);
157
158         if (gf != NULL) {
159                 EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY);
160                 found = true;
161                 unregister_groups(gf);
162                 /* TODO: zero pointer first */
163                 free_family(gf);
164                 bzero(gf, sizeof(*gf));
165         }
166         GENL_UNLOCK();
167
168         return (found);
169 }
170
171 bool
172 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count)
173 {
174         GENL_LOCK();
175         struct genl_family *gf = find_family(family_name);
176         if (gf == NULL) {
177                 GENL_UNLOCK();
178                 return (false);
179         }
180
181         int cmd_size = gf->family_cmd_size;
182
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;
187         }
188
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);
193
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);
199         }
200
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);
207         }
208         GENL_UNLOCK();
209         return (true);
210 }
211
212 static struct genl_group *
213 find_group(const struct genl_family *gf, const char *group_name)
214 {
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))
218                         return (gg);
219         }
220         return (NULL);
221 }
222
223 uint32_t
224 genl_register_group(const char *family_name, const char *group_name)
225 {
226         uint32_t group_id = 0;
227
228         MPASS(family_name != NULL);
229         MPASS(group_name != NULL);
230
231         GENL_LOCK();
232         struct genl_family *gf = find_family(family_name);
233
234         if (gf == NULL || find_group(gf, group_name) != NULL) {
235                 GENL_UNLOCK();
236                 return (0);
237         }
238
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;
246                         break;
247                 }
248         }
249         GENL_UNLOCK();
250
251         return (group_id);
252 }
253
254 /* accessors */
255 struct genl_family *
256 genl_get_family(uint32_t family_id)
257 {
258         return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL);
259 }
260
261 const char *
262 genl_get_family_name(const struct genl_family *gf)
263 {
264         return (gf->family_name);
265 }
266
267 uint32_t
268 genl_get_family_id(const struct genl_family *gf)
269 {
270         return (gf->family_id);
271 }
272
273 struct genl_group *
274 genl_get_group(uint32_t group_id)
275 {
276         return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL);
277 }
278