1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Alexander V. Chernikov 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 "opt_netlink.h" 29 30 #include <sys/param.h> 31 #include <sys/refcount.h> 32 #include <sys/types.h> 33 #include <sys/kernel.h> 34 #include <sys/lock.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/socket.h> 38 #include <sys/priv.h> 39 40 #include <netlink/netlink.h> 41 #include <netlink/netlink_ctl.h> 42 #include <netlink/netlink_generic.h> 43 #include <netlink/netlink_message_parser.h> 44 45 #include <machine/stdarg.h> 46 #include <tests/ktest.h> 47 48 struct mtx ktest_mtx; 49 #define KTEST_LOCK() mtx_lock(&ktest_mtx) 50 #define KTEST_UNLOCK() mtx_unlock(&ktest_mtx) 51 #define KTEST_LOCK_ASSERT() mtx_assert(&ktest_mtx, MA_OWNED) 52 53 MTX_SYSINIT(ktest_mtx, &ktest_mtx, "ktest mutex", MTX_DEF); 54 55 struct ktest_module { 56 struct ktest_module_info *info; 57 volatile u_int refcount; 58 TAILQ_ENTRY(ktest_module) entries; 59 }; 60 static TAILQ_HEAD(, ktest_module) module_list = TAILQ_HEAD_INITIALIZER(module_list); 61 62 struct nl_ktest_parsed { 63 char *mod_name; 64 char *test_name; 65 struct nlattr *test_meta; 66 }; 67 68 #define _IN(_field) offsetof(struct genlmsghdr, _field) 69 #define _OUT(_field) offsetof(struct nl_ktest_parsed, _field) 70 71 static const struct nlattr_parser nla_p_get[] = { 72 { .type = KTEST_ATTR_MOD_NAME, .off = _OUT(mod_name), .cb = nlattr_get_string }, 73 { .type = KTEST_ATTR_TEST_NAME, .off = _OUT(test_name), .cb = nlattr_get_string }, 74 { .type = KTEST_ATTR_TEST_META, .off = _OUT(test_meta), .cb = nlattr_get_nla }, 75 }; 76 static const struct nlfield_parser nlf_p_get[] = { 77 }; 78 NL_DECLARE_PARSER(ktest_parser, struct genlmsghdr, nlf_p_get, nla_p_get); 79 #undef _IN 80 #undef _OUT 81 82 static bool 83 create_reply(struct nl_writer *nw, struct nlmsghdr *hdr, int cmd) 84 { 85 if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 86 return (false); 87 88 struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 89 ghdr_new->cmd = cmd; 90 ghdr_new->version = 0; 91 ghdr_new->reserved = 0; 92 93 return (true); 94 } 95 96 static int 97 dump_mod_test(struct nlmsghdr *hdr, struct nl_pstate *npt, 98 struct ktest_module *mod, const struct ktest_test_info *test_info) 99 { 100 struct nl_writer *nw = npt->nw; 101 102 if (!create_reply(nw, hdr, KTEST_CMD_NEWTEST)) 103 goto enomem; 104 105 nlattr_add_string(nw, KTEST_ATTR_MOD_NAME, mod->info->name); 106 nlattr_add_string(nw, KTEST_ATTR_TEST_NAME, test_info->name); 107 nlattr_add_string(nw, KTEST_ATTR_TEST_DESCR, test_info->desc); 108 109 if (nlmsg_end(nw)) 110 return (0); 111 enomem: 112 nlmsg_abort(nw); 113 return (ENOMEM); 114 } 115 116 static int 117 dump_mod_tests(struct nlmsghdr *hdr, struct nl_pstate *npt, 118 struct ktest_module *mod, struct nl_ktest_parsed *attrs) 119 { 120 for (int i = 0; i < mod->info->num_tests; i++) { 121 const struct ktest_test_info *test_info = &mod->info->tests[i]; 122 if (attrs->test_name != NULL && strcmp(attrs->test_name, test_info->name)) 123 continue; 124 int error = dump_mod_test(hdr, npt, mod, test_info); 125 if (error != 0) 126 return (error); 127 } 128 129 return (0); 130 } 131 132 static int 133 dump_tests(struct nlmsghdr *hdr, struct nl_pstate *npt) 134 { 135 struct nl_ktest_parsed attrs = { }; 136 struct ktest_module *mod; 137 int error; 138 139 error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs); 140 if (error != 0) 141 return (error); 142 143 hdr->nlmsg_flags |= NLM_F_MULTI; 144 145 KTEST_LOCK(); 146 TAILQ_FOREACH(mod, &module_list, entries) { 147 if (attrs.mod_name && strcmp(attrs.mod_name, mod->info->name)) 148 continue; 149 error = dump_mod_tests(hdr, npt, mod, &attrs); 150 if (error != 0) 151 break; 152 } 153 KTEST_UNLOCK(); 154 155 if (!nlmsg_end_dump(npt->nw, error, hdr)) { 156 //NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 157 return (ENOMEM); 158 } 159 160 return (error); 161 } 162 163 static int 164 run_test(struct nlmsghdr *hdr, struct nl_pstate *npt) 165 { 166 struct nl_ktest_parsed attrs = { }; 167 struct ktest_module *mod; 168 int error; 169 170 error = nl_parse_nlmsg(hdr, &ktest_parser, npt, &attrs); 171 if (error != 0) 172 return (error); 173 174 if (attrs.mod_name == NULL) { 175 nlmsg_report_err_msg(npt, "KTEST_ATTR_MOD_NAME not set"); 176 return (EINVAL); 177 } 178 179 if (attrs.test_name == NULL) { 180 nlmsg_report_err_msg(npt, "KTEST_ATTR_TEST_NAME not set"); 181 return (EINVAL); 182 } 183 184 const struct ktest_test_info *test = NULL; 185 186 KTEST_LOCK(); 187 TAILQ_FOREACH(mod, &module_list, entries) { 188 if (strcmp(attrs.mod_name, mod->info->name)) 189 continue; 190 191 const struct ktest_module_info *info = mod->info; 192 193 for (int i = 0; i < info->num_tests; i++) { 194 const struct ktest_test_info *test_info = &info->tests[i]; 195 196 if (!strcmp(attrs.test_name, test_info->name)) { 197 test = test_info; 198 break; 199 } 200 } 201 break; 202 } 203 if (test != NULL) 204 refcount_acquire(&mod->refcount); 205 KTEST_UNLOCK(); 206 207 if (test == NULL) 208 return (ESRCH); 209 210 /* Run the test */ 211 struct ktest_test_context ctx = { 212 .npt = npt, 213 .hdr = hdr, 214 .buf = npt_alloc(npt, KTEST_MAX_BUF), 215 .bufsize = KTEST_MAX_BUF, 216 }; 217 218 if (ctx.buf == NULL) { 219 //NL_LOG(LOG_DEBUG, "unable to allocate temporary buffer"); 220 return (ENOMEM); 221 } 222 223 if (test->parse != NULL && attrs.test_meta != NULL) { 224 error = test->parse(&ctx, attrs.test_meta); 225 if (error != 0) 226 return (error); 227 } 228 229 hdr->nlmsg_flags |= NLM_F_MULTI; 230 231 KTEST_LOG_LEVEL(&ctx, LOG_INFO, "start running %s", test->name); 232 error = test->func(&ctx); 233 KTEST_LOG_LEVEL(&ctx, LOG_INFO, "end running %s", test->name); 234 235 refcount_release(&mod->refcount); 236 237 if (!nlmsg_end_dump(npt->nw, error, hdr)) { 238 //NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 239 return (ENOMEM); 240 } 241 242 return (error); 243 } 244 245 246 /* USER API */ 247 static void 248 register_test_module(struct ktest_module_info *info) 249 { 250 struct ktest_module *mod = malloc(sizeof(*mod), M_TEMP, M_WAITOK | M_ZERO); 251 252 mod->info = info; 253 info->module_ptr = mod; 254 KTEST_LOCK(); 255 TAILQ_INSERT_TAIL(&module_list, mod, entries); 256 KTEST_UNLOCK(); 257 } 258 259 static void 260 unregister_test_module(struct ktest_module_info *info) 261 { 262 struct ktest_module *mod = info->module_ptr; 263 264 info->module_ptr = NULL; 265 266 KTEST_LOCK(); 267 TAILQ_REMOVE(&module_list, mod, entries); 268 KTEST_UNLOCK(); 269 270 free(mod, M_TEMP); 271 } 272 273 static bool 274 can_unregister(struct ktest_module_info *info) 275 { 276 struct ktest_module *mod = info->module_ptr; 277 278 return (refcount_load(&mod->refcount) == 0); 279 } 280 281 int 282 ktest_default_modevent(module_t mod, int type, void *arg) 283 { 284 struct ktest_module_info *info = (struct ktest_module_info *)arg; 285 int error = 0; 286 287 switch (type) { 288 case MOD_LOAD: 289 register_test_module(info); 290 break; 291 case MOD_UNLOAD: 292 if (!can_unregister(info)) 293 return (EBUSY); 294 unregister_test_module(info); 295 break; 296 default: 297 error = EOPNOTSUPP; 298 break; 299 } 300 return (error); 301 } 302 303 bool 304 ktest_start_msg(struct ktest_test_context *ctx) 305 { 306 return (create_reply(ctx->npt->nw, ctx->hdr, KTEST_CMD_NEWMESSAGE)); 307 } 308 309 void 310 ktest_add_msg_meta(struct ktest_test_context *ctx, const char *func, 311 const char *fname, int line) 312 { 313 struct nl_writer *nw = ctx->npt->nw; 314 struct timespec ts; 315 316 nanouptime(&ts); 317 nlattr_add(nw, KTEST_MSG_ATTR_TS, sizeof(ts), &ts); 318 319 nlattr_add_string(nw, KTEST_MSG_ATTR_FUNC, func); 320 nlattr_add_string(nw, KTEST_MSG_ATTR_FILE, fname); 321 nlattr_add_u32(nw, KTEST_MSG_ATTR_LINE, line); 322 } 323 324 void 325 ktest_add_msg_text(struct ktest_test_context *ctx, int msg_level, 326 const char *fmt, ...) 327 { 328 va_list ap; 329 330 va_start(ap, fmt); 331 vsnprintf(ctx->buf, ctx->bufsize, fmt, ap); 332 va_end(ap); 333 334 nlattr_add_u8(ctx->npt->nw, KTEST_MSG_ATTR_LEVEL, msg_level); 335 nlattr_add_string(ctx->npt->nw, KTEST_MSG_ATTR_TEXT, ctx->buf); 336 } 337 338 void 339 ktest_end_msg(struct ktest_test_context *ctx) 340 { 341 nlmsg_end(ctx->npt->nw); 342 } 343 344 /* Module glue */ 345 346 static const struct nlhdr_parser *all_parsers[] = { &ktest_parser }; 347 348 static const struct genl_cmd ktest_cmds[] = { 349 { 350 .cmd_num = KTEST_CMD_LIST, 351 .cmd_name = "KTEST_CMD_LIST", 352 .cmd_cb = dump_tests, 353 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 354 }, 355 { 356 .cmd_num = KTEST_CMD_RUN, 357 .cmd_name = "KTEST_CMD_RUN", 358 .cmd_cb = run_test, 359 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, 360 .cmd_priv = PRIV_KLD_LOAD, 361 }, 362 }; 363 364 static void 365 ktest_nl_register(void) 366 { 367 bool ret __diagused; 368 int family_id __diagused; 369 370 NL_VERIFY_PARSERS(all_parsers); 371 family_id = genl_register_family(KTEST_FAMILY_NAME, 0, 1, KTEST_CMD_MAX); 372 MPASS(family_id != 0); 373 374 ret = genl_register_cmds(KTEST_FAMILY_NAME, ktest_cmds, NL_ARRAY_LEN(ktest_cmds)); 375 MPASS(ret); 376 } 377 378 static void 379 ktest_nl_unregister(void) 380 { 381 MPASS(TAILQ_EMPTY(&module_list)); 382 383 genl_unregister_family(KTEST_FAMILY_NAME); 384 } 385 386 static int 387 ktest_modevent(module_t mod, int type, void *unused) 388 { 389 int error = 0; 390 391 switch (type) { 392 case MOD_LOAD: 393 ktest_nl_register(); 394 break; 395 case MOD_UNLOAD: 396 ktest_nl_unregister(); 397 break; 398 default: 399 error = EOPNOTSUPP; 400 break; 401 } 402 return (error); 403 } 404 405 static moduledata_t ktestmod = { 406 "ktest", 407 ktest_modevent, 408 0 409 }; 410 411 DECLARE_MODULE(ktestmod, ktestmod, SI_SUB_PSEUDO, SI_ORDER_ANY); 412 MODULE_VERSION(ktestmod, 1); 413 MODULE_DEPEND(ktestmod, netlink, 1, 1, 1); 414 415