18a2af0b4SBaptiste Daroussin /*- 28a2af0b4SBaptiste Daroussin * SPDX-License-Identifier: BSD-2-Clause 38a2af0b4SBaptiste Daroussin * 48a2af0b4SBaptiste Daroussin * Copyright (c) 2023 Baptiste Daroussin <bapt@FreeBSD.org> 58a2af0b4SBaptiste Daroussin * 68a2af0b4SBaptiste Daroussin * Redistribution and use in source and binary forms, with or without 78a2af0b4SBaptiste Daroussin * modification, are permitted provided that the following conditions 88a2af0b4SBaptiste Daroussin * are met: 98a2af0b4SBaptiste Daroussin * 1. Redistributions of source code must retain the above copyright 108a2af0b4SBaptiste Daroussin * notice, this list of conditions and the following disclaimer. 118a2af0b4SBaptiste Daroussin * 2. Redistributions in binary form must reproduce the above copyright 128a2af0b4SBaptiste Daroussin * notice, this list of conditions and the following disclaimer in the 138a2af0b4SBaptiste Daroussin * documentation and/or other materials provided with the distribution. 148a2af0b4SBaptiste Daroussin * 158a2af0b4SBaptiste Daroussin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 168a2af0b4SBaptiste Daroussin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 178a2af0b4SBaptiste Daroussin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 188a2af0b4SBaptiste Daroussin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 198a2af0b4SBaptiste Daroussin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 208a2af0b4SBaptiste Daroussin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 218a2af0b4SBaptiste Daroussin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 228a2af0b4SBaptiste Daroussin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 238a2af0b4SBaptiste Daroussin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 248a2af0b4SBaptiste Daroussin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 258a2af0b4SBaptiste Daroussin * SUCH DAMAGE. 268a2af0b4SBaptiste Daroussin */ 278a2af0b4SBaptiste Daroussin 288a2af0b4SBaptiste Daroussin #include <sys/param.h> 298a2af0b4SBaptiste Daroussin #include <sys/types.h> 308a2af0b4SBaptiste Daroussin #include <sys/devctl.h> 318a2af0b4SBaptiste Daroussin #include <sys/errno.h> 328a2af0b4SBaptiste Daroussin #include <sys/module.h> 338a2af0b4SBaptiste Daroussin #include <sys/kernel.h> 348a2af0b4SBaptiste Daroussin #include <sys/malloc.h> 358a2af0b4SBaptiste Daroussin #include <net/vnet.h> 368a2af0b4SBaptiste Daroussin #include <netlink/netlink.h> 378a2af0b4SBaptiste Daroussin #include <netlink/netlink_ctl.h> 388a2af0b4SBaptiste Daroussin #include <netlink/netlink_generic.h> 39*cef0bbaeSBaptiste Daroussin #include <netlink/netlink_sysevent.h> 408a2af0b4SBaptiste Daroussin 418a2af0b4SBaptiste Daroussin #define DEBUG_MOD_NAME nl_sysevent 428a2af0b4SBaptiste Daroussin #define DEBUG_MAX_LEVEL LOG_DEBUG3 438a2af0b4SBaptiste Daroussin #include <netlink/netlink_debug.h> 448a2af0b4SBaptiste Daroussin _DECLARE_DEBUG(LOG_INFO); 458a2af0b4SBaptiste Daroussin 468a2af0b4SBaptiste Daroussin MALLOC_DEFINE(M_NLSE, "nlsysevent", "Memory used for Netlink sysevent"); 478a2af0b4SBaptiste Daroussin #define NLSE_FAMILY_NAME "nlsysevent" 488a2af0b4SBaptiste Daroussin static uint32_t ctrl_family_id; 498a2af0b4SBaptiste Daroussin 508a2af0b4SBaptiste Daroussin #define MAX_SYSEVENTS 64 518a2af0b4SBaptiste Daroussin static struct sysevent { 528a2af0b4SBaptiste Daroussin char *name; 538a2af0b4SBaptiste Daroussin uint32_t id; 548a2af0b4SBaptiste Daroussin } sysevents[MAX_SYSEVENTS] = {}; 558a2af0b4SBaptiste Daroussin 568a2af0b4SBaptiste Daroussin static void 578a2af0b4SBaptiste Daroussin sysevent_write(struct sysevent *se, const char *subsystem, const char *type, 588a2af0b4SBaptiste Daroussin const char *data) 598a2af0b4SBaptiste Daroussin { 608a2af0b4SBaptiste Daroussin struct nl_writer nw = {}; 618a2af0b4SBaptiste Daroussin 628a2af0b4SBaptiste Daroussin if (!nlmsg_get_group_writer(&nw, NLMSG_LARGE, NETLINK_GENERIC, se->id)) { 638a2af0b4SBaptiste Daroussin NL_LOG(LOG_DEBUG, "error allocating group writer"); 648a2af0b4SBaptiste Daroussin return; 658a2af0b4SBaptiste Daroussin } 668a2af0b4SBaptiste Daroussin struct nlmsghdr hdr = { .nlmsg_type = ctrl_family_id }; 678a2af0b4SBaptiste Daroussin if (!nlmsg_reply(&nw, &hdr, sizeof(struct genlmsghdr))) { 688a2af0b4SBaptiste Daroussin return; 698a2af0b4SBaptiste Daroussin } 708a2af0b4SBaptiste Daroussin 718a2af0b4SBaptiste Daroussin struct genlmsghdr *ghdr = nlmsg_reserve_object(&nw, struct genlmsghdr); 728a2af0b4SBaptiste Daroussin if (ghdr == NULL) { 738a2af0b4SBaptiste Daroussin NL_LOG(LOG_DEBUG, "unable to allocate memory"); 748a2af0b4SBaptiste Daroussin return; 758a2af0b4SBaptiste Daroussin } 768a2af0b4SBaptiste Daroussin ghdr->version = 0; 778a2af0b4SBaptiste Daroussin ghdr->cmd = 0; 788a2af0b4SBaptiste Daroussin ghdr->reserved = 0; 798a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_SYSTEM, se->name); 808a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_SUBSYSTEM, subsystem); 818a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_TYPE, type); 828a2af0b4SBaptiste Daroussin if (data != NULL) 838a2af0b4SBaptiste Daroussin nlattr_add_string(&nw, NLSE_ATTR_DATA, data); 848a2af0b4SBaptiste Daroussin nlmsg_end(&nw); 858a2af0b4SBaptiste Daroussin nlmsg_flush(&nw); 868a2af0b4SBaptiste Daroussin } 878a2af0b4SBaptiste Daroussin 888a2af0b4SBaptiste Daroussin static void 898a2af0b4SBaptiste Daroussin sysevent_send(const char *system, const char *subsystem, const char *type, 908a2af0b4SBaptiste Daroussin const char *data) 918a2af0b4SBaptiste Daroussin { 928a2af0b4SBaptiste Daroussin struct sysevent *se = NULL; 938a2af0b4SBaptiste Daroussin 948a2af0b4SBaptiste Daroussin for (size_t i = 0; i < MAX_SYSEVENTS; i++) { 958a2af0b4SBaptiste Daroussin if (sysevents[i].name == NULL) { 968a2af0b4SBaptiste Daroussin sysevents[i].name = strdup(system, M_NLSE); 978a2af0b4SBaptiste Daroussin sysevents[i].id = genl_register_group(NLSE_FAMILY_NAME, 988a2af0b4SBaptiste Daroussin system); 998a2af0b4SBaptiste Daroussin se = &sysevents[i]; 1008a2af0b4SBaptiste Daroussin break; 1018a2af0b4SBaptiste Daroussin } 1028a2af0b4SBaptiste Daroussin if (strcmp(sysevents[i].name, system) == 0) { 1038a2af0b4SBaptiste Daroussin se = &sysevents[i]; 1048a2af0b4SBaptiste Daroussin break; 1058a2af0b4SBaptiste Daroussin } 1068a2af0b4SBaptiste Daroussin } 1078a2af0b4SBaptiste Daroussin if (se == NULL) { 1088a2af0b4SBaptiste Daroussin NL_LOG(LOG_WARNING, "impossible to add the event %s, " 1098a2af0b4SBaptiste Daroussin "too many events\n", system); 1108a2af0b4SBaptiste Daroussin return; 1118a2af0b4SBaptiste Daroussin } 1128a2af0b4SBaptiste Daroussin 1138a2af0b4SBaptiste Daroussin CURVNET_SET(vnet0); 1148a2af0b4SBaptiste Daroussin sysevent_write(se, subsystem, type, data); 1158a2af0b4SBaptiste Daroussin CURVNET_RESTORE(); 1168a2af0b4SBaptiste Daroussin } 1178a2af0b4SBaptiste Daroussin 1188a2af0b4SBaptiste Daroussin static void 1198a2af0b4SBaptiste Daroussin nlsysevent_load(void) 1208a2af0b4SBaptiste Daroussin { 1218a2af0b4SBaptiste Daroussin devctl_set_notify_hook(sysevent_send); 1228a2af0b4SBaptiste Daroussin ctrl_family_id = genl_register_family(NLSE_FAMILY_NAME, 0, 2, NLSE_ATTR_MAX); 1238a2af0b4SBaptiste Daroussin for (size_t i = 0; i < nitems(devctl_systems); i++) { 1248a2af0b4SBaptiste Daroussin if (i >= MAX_SYSEVENTS) { 1258a2af0b4SBaptiste Daroussin NL_LOG(LOG_WARNING, "impossible to add the event %s, too many events\n", devctl_systems[i]); 1268a2af0b4SBaptiste Daroussin continue; 1278a2af0b4SBaptiste Daroussin } 1288a2af0b4SBaptiste Daroussin sysevents[i].name = strdup(devctl_systems[i], M_NLSE); 1298a2af0b4SBaptiste Daroussin sysevents[i].id = genl_register_group(NLSE_FAMILY_NAME, devctl_systems[i]); 1308a2af0b4SBaptiste Daroussin } 1318a2af0b4SBaptiste Daroussin } 1328a2af0b4SBaptiste Daroussin 1338a2af0b4SBaptiste Daroussin static void 1348a2af0b4SBaptiste Daroussin nlsysevent_unload(void) 1358a2af0b4SBaptiste Daroussin { 1368a2af0b4SBaptiste Daroussin devctl_unset_notify_hook(); 1378a2af0b4SBaptiste Daroussin genl_unregister_family(NLSE_FAMILY_NAME); 1388a2af0b4SBaptiste Daroussin for (size_t i = 0; i < MAX_SYSEVENTS; i++) { 1398a2af0b4SBaptiste Daroussin if (sysevents[i].name == NULL) 1408a2af0b4SBaptiste Daroussin break; 1418a2af0b4SBaptiste Daroussin free(sysevents[i].name, M_NLSE); 1428a2af0b4SBaptiste Daroussin } 1438a2af0b4SBaptiste Daroussin } 1448a2af0b4SBaptiste Daroussin 1458a2af0b4SBaptiste Daroussin static int 1468a2af0b4SBaptiste Daroussin nlsysevent_loader(module_t mod __unused, int what, void *priv __unused) 1478a2af0b4SBaptiste Daroussin { 1488a2af0b4SBaptiste Daroussin int err = 0; 1498a2af0b4SBaptiste Daroussin 1508a2af0b4SBaptiste Daroussin switch (what) { 1518a2af0b4SBaptiste Daroussin case MOD_LOAD: 1528a2af0b4SBaptiste Daroussin nlsysevent_load(); 1538a2af0b4SBaptiste Daroussin break; 1548a2af0b4SBaptiste Daroussin case MOD_UNLOAD: 1558a2af0b4SBaptiste Daroussin nlsysevent_unload(); 1568a2af0b4SBaptiste Daroussin break; 1578a2af0b4SBaptiste Daroussin default: 1588a2af0b4SBaptiste Daroussin err = EOPNOTSUPP; 1598a2af0b4SBaptiste Daroussin break; 1608a2af0b4SBaptiste Daroussin } 1618a2af0b4SBaptiste Daroussin return (err); 1628a2af0b4SBaptiste Daroussin } 1638a2af0b4SBaptiste Daroussin static moduledata_t nlsysevent_mod = { "nlsysevent", nlsysevent_loader, NULL}; 1648a2af0b4SBaptiste Daroussin 1658a2af0b4SBaptiste Daroussin DECLARE_MODULE(nlsysevent, nlsysevent_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 1668a2af0b4SBaptiste Daroussin MODULE_DEPEND(nlsysevent, netlink, 1, 1, 1); 1678a2af0b4SBaptiste Daroussin MODULE_VERSION(nlsysevent, 1); 168