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