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, 97 uint16_t family_version, uint16_t max_attr_idx) 98 { 99 100 MPASS(family_name != NULL); 101 if (find_family(family_name) != NULL) 102 return (0); 103 104 GENL_LOCK(); 105 106 struct genl_family *gf = find_empty_family_id(family_name); 107 MPASS(gf != NULL); 108 109 gf->family_name = family_name; 110 gf->family_version = family_version; 111 gf->family_hdrsize = hdrsize; 112 gf->family_attr_max = max_attr_idx; 113 NL_LOG(LOG_DEBUG2, "Registered family %s id %d", gf->family_name, 114 gf->family_id); 115 EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY); 116 117 GENL_UNLOCK(); 118 119 return (gf->family_id); 120 } 121 122 static void 123 free_family(struct genl_family *gf) 124 { 125 if (gf->family_cmds != NULL) 126 free(gf->family_cmds, M_NETLINK); 127 } 128 129 /* 130 * unregister groups of a given family 131 */ 132 static void 133 unregister_groups(const struct genl_family *gf) 134 { 135 136 for (int i = 0; i < MAX_GROUPS; i++) { 137 struct genl_group *gg = &groups[i]; 138 if (gg->group_family == gf && gg->group_name != NULL) { 139 gg->group_family = NULL; 140 gg->group_name = NULL; 141 } 142 } 143 } 144 145 /* 146 * Can sleep, I guess 147 */ 148 bool 149 genl_unregister_family(const char *family_name) 150 { 151 bool found = false; 152 153 GENL_LOCK(); 154 struct genl_family *gf = find_family(family_name); 155 156 if (gf != NULL) { 157 EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY); 158 found = true; 159 unregister_groups(gf); 160 /* TODO: zero pointer first */ 161 free_family(gf); 162 bzero(gf, sizeof(*gf)); 163 } 164 GENL_UNLOCK(); 165 166 return (found); 167 } 168 169 bool 170 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count) 171 { 172 GENL_LOCK(); 173 struct genl_family *gf = find_family(family_name); 174 if (gf == NULL) { 175 GENL_UNLOCK(); 176 return (false); 177 } 178 179 int cmd_size = gf->family_cmd_size; 180 181 for (int i = 0; i < count; i++) { 182 MPASS(cmds[i].cmd_cb != NULL); 183 if (cmds[i].cmd_num >= cmd_size) 184 cmd_size = cmds[i].cmd_num + 1; 185 } 186 187 if (cmd_size > gf->family_cmd_size) { 188 /* need to realloc */ 189 size_t sz = cmd_size * sizeof(struct genl_cmd); 190 void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO); 191 192 memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd)); 193 void *old_data = gf->family_cmds; 194 gf->family_cmds = data; 195 gf->family_cmd_size = cmd_size; 196 free(old_data, M_NETLINK); 197 } 198 199 for (int i = 0; i < count; i++) { 200 const struct genl_cmd *cmd = &cmds[i]; 201 MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL); 202 gf->family_cmds[cmd->cmd_num] = cmds[i]; 203 NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s", 204 cmd->cmd_name, cmd->cmd_num, gf->family_name); 205 } 206 GENL_UNLOCK(); 207 return (true); 208 } 209 210 static struct genl_group * 211 find_group(const struct genl_family *gf, const char *group_name) 212 { 213 for (int i = 0; i < MAX_GROUPS; i++) { 214 struct genl_group *gg = &groups[i]; 215 if (gg->group_family == gf && !strcmp(gg->group_name, group_name)) 216 return (gg); 217 } 218 return (NULL); 219 } 220 221 uint32_t 222 genl_register_group(const char *family_name, const char *group_name) 223 { 224 uint32_t group_id = 0; 225 226 MPASS(family_name != NULL); 227 MPASS(group_name != NULL); 228 229 GENL_LOCK(); 230 struct genl_family *gf = find_family(family_name); 231 232 if (gf == NULL || find_group(gf, group_name) != NULL) { 233 GENL_UNLOCK(); 234 return (0); 235 } 236 237 for (int i = 0; i < MAX_GROUPS; i++) { 238 struct genl_group *gg = &groups[i]; 239 if (gg->group_family == NULL) { 240 gf->family_num_groups++; 241 gg->group_family = gf; 242 gg->group_name = group_name; 243 group_id = i + MIN_GROUP_NUM; 244 break; 245 } 246 } 247 GENL_UNLOCK(); 248 249 return (group_id); 250 } 251 252 /* accessors */ 253 struct genl_family * 254 genl_get_family(uint16_t family_id) 255 { 256 return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL); 257 } 258 259 const char * 260 genl_get_family_name(const struct genl_family *gf) 261 { 262 return (gf->family_name); 263 } 264 265 uint16_t 266 genl_get_family_id(const struct genl_family *gf) 267 { 268 return (gf->family_id); 269 } 270 271 struct genl_group * 272 genl_get_group(uint32_t group_id) 273 { 274 return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL); 275 } 276 277