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 279