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