17e5bf684SAlexander V. Chernikov /*- 27e5bf684SAlexander V. Chernikov * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 37e5bf684SAlexander V. Chernikov * 47e5bf684SAlexander V. Chernikov * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org> 57e5bf684SAlexander V. Chernikov * 67e5bf684SAlexander V. Chernikov * Redistribution and use in source and binary forms, with or without 77e5bf684SAlexander V. Chernikov * modification, are permitted provided that the following conditions 87e5bf684SAlexander V. Chernikov * are met: 97e5bf684SAlexander V. Chernikov * 1. Redistributions of source code must retain the above copyright 107e5bf684SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer. 117e5bf684SAlexander V. Chernikov * 2. Redistributions in binary form must reproduce the above copyright 127e5bf684SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer in the 137e5bf684SAlexander V. Chernikov * documentation and/or other materials provided with the distribution. 147e5bf684SAlexander V. Chernikov * 157e5bf684SAlexander V. Chernikov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 167e5bf684SAlexander V. Chernikov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177e5bf684SAlexander V. Chernikov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187e5bf684SAlexander V. Chernikov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197e5bf684SAlexander V. Chernikov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207e5bf684SAlexander V. Chernikov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217e5bf684SAlexander V. Chernikov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227e5bf684SAlexander V. Chernikov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237e5bf684SAlexander V. Chernikov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247e5bf684SAlexander V. Chernikov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257e5bf684SAlexander V. Chernikov * SUCH DAMAGE. 267e5bf684SAlexander V. Chernikov */ 277e5bf684SAlexander V. Chernikov 2819e43c16SAlexander V. Chernikov #include "opt_netlink.h" 2919e43c16SAlexander V. Chernikov 307e5bf684SAlexander V. Chernikov #include <sys/cdefs.h> 317e5bf684SAlexander V. Chernikov __FBSDID("$FreeBSD$"); 327e5bf684SAlexander V. Chernikov #include <sys/types.h> 33fc083c3eSJung-uk Kim #include <sys/ck.h> 34669d63ebSAlexander V. Chernikov #include <sys/epoch.h> 3519e43c16SAlexander V. Chernikov #include <sys/eventhandler.h> 36fc083c3eSJung-uk Kim #include <sys/kernel.h> 3704f75b98SAlexander V. Chernikov #include <sys/jail.h> 38fc083c3eSJung-uk Kim #include <sys/lock.h> 397e5bf684SAlexander V. Chernikov #include <sys/malloc.h> 407e5bf684SAlexander V. Chernikov #include <sys/priv.h> 417e5bf684SAlexander V. Chernikov #include <sys/socket.h> 42fc083c3eSJung-uk Kim #include <sys/sx.h> 437e5bf684SAlexander V. Chernikov 447e5bf684SAlexander V. Chernikov #include <netlink/netlink.h> 457e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 467e5bf684SAlexander V. Chernikov #include <netlink/netlink_generic.h> 4719e43c16SAlexander V. Chernikov #include <netlink/netlink_var.h> 487e5bf684SAlexander V. Chernikov 497e5bf684SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_generic 507e5bf684SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3 517e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h> 52*fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO); 537e5bf684SAlexander V. Chernikov 547e5bf684SAlexander V. Chernikov static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 557e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw); 567e5bf684SAlexander V. Chernikov 577e5bf684SAlexander V. Chernikov /* 587e5bf684SAlexander V. Chernikov * Handler called by netlink subsystem when matching netlink message is received 597e5bf684SAlexander V. Chernikov */ 607e5bf684SAlexander V. Chernikov static int 617e5bf684SAlexander V. Chernikov genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) 627e5bf684SAlexander V. Chernikov { 637e5bf684SAlexander V. Chernikov struct nlpcb *nlp = npt->nlp; 6419e43c16SAlexander V. Chernikov struct genl_family *gf = NULL; 657e5bf684SAlexander V. Chernikov int error = 0; 667e5bf684SAlexander V. Chernikov 677e5bf684SAlexander V. Chernikov int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID; 687e5bf684SAlexander V. Chernikov 6919e43c16SAlexander V. Chernikov if (__predict_false(family_id < 0 || (gf = genl_get_family(family_id)) == NULL)) { 707e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type); 717e5bf684SAlexander V. Chernikov return (ENOTSUP); 727e5bf684SAlexander V. Chernikov } 737e5bf684SAlexander V. Chernikov 747e5bf684SAlexander V. Chernikov if (__predict_false(hdr->nlmsg_len < sizeof(hdr) + GENL_HDRLEN)) { 757e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len); 767e5bf684SAlexander V. Chernikov return (EINVAL); 777e5bf684SAlexander V. Chernikov } 787e5bf684SAlexander V. Chernikov 797e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); 807e5bf684SAlexander V. Chernikov 817e5bf684SAlexander V. Chernikov if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { 827e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d", 837e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 847e5bf684SAlexander V. Chernikov return (ENOTSUP); 857e5bf684SAlexander V. Chernikov } 867e5bf684SAlexander V. Chernikov 877e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd]; 887e5bf684SAlexander V. Chernikov 897e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) { 907e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed", 917e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 927e5bf684SAlexander V. Chernikov return (EPERM); 937e5bf684SAlexander V. Chernikov } 947e5bf684SAlexander V. Chernikov 957e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d", 967e5bf684SAlexander V. Chernikov gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len); 977e5bf684SAlexander V. Chernikov 987e5bf684SAlexander V. Chernikov error = cmd->cmd_cb(hdr, npt); 997e5bf684SAlexander V. Chernikov 1007e5bf684SAlexander V. Chernikov return (error); 1017e5bf684SAlexander V. Chernikov } 1027e5bf684SAlexander V. Chernikov 1037e5bf684SAlexander V. Chernikov static uint32_t 1047e5bf684SAlexander V. Chernikov get_cmd_flags(const struct genl_cmd *cmd) 1057e5bf684SAlexander V. Chernikov { 1067e5bf684SAlexander V. Chernikov uint32_t flags = cmd->cmd_flags; 1077e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0) 1087e5bf684SAlexander V. Chernikov flags |= GENL_ADMIN_PERM; 1097e5bf684SAlexander V. Chernikov return (flags); 1107e5bf684SAlexander V. Chernikov } 1117e5bf684SAlexander V. Chernikov 1127e5bf684SAlexander V. Chernikov static int 1137e5bf684SAlexander V. Chernikov dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 1147e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw) 1157e5bf684SAlexander V. Chernikov { 1167e5bf684SAlexander V. Chernikov if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 1177e5bf684SAlexander V. Chernikov goto enomem; 1187e5bf684SAlexander V. Chernikov 1197e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 1207e5bf684SAlexander V. Chernikov ghdr_new->cmd = ghdr->cmd; 1217e5bf684SAlexander V. Chernikov ghdr_new->version = gf->family_version; 1227e5bf684SAlexander V. Chernikov ghdr_new->reserved = 0; 1237e5bf684SAlexander V. Chernikov 1247e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name); 1257e5bf684SAlexander V. Chernikov nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, gf->family_id); 1267e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version); 1277e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize); 1287e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max); 1297e5bf684SAlexander V. Chernikov 1307e5bf684SAlexander V. Chernikov if (gf->family_cmd_size > 0) { 1317e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_OPS); 1327e5bf684SAlexander V. Chernikov if (off == 0) 1337e5bf684SAlexander V. Chernikov goto enomem; 1347e5bf684SAlexander V. Chernikov for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) { 1357e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[i]; 1367e5bf684SAlexander V. Chernikov if (cmd->cmd_cb == NULL) 1377e5bf684SAlexander V. Chernikov continue; 1387e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 1397e5bf684SAlexander V. Chernikov if (cmd_off == 0) 1407e5bf684SAlexander V. Chernikov goto enomem; 1417e5bf684SAlexander V. Chernikov 1427e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num); 1437e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd)); 1447e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 1457e5bf684SAlexander V. Chernikov } 1467e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 1477e5bf684SAlexander V. Chernikov } 1487e5bf684SAlexander V. Chernikov if (gf->family_num_groups > 0) { 1497e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS); 1507e5bf684SAlexander V. Chernikov if (off == 0) 1517e5bf684SAlexander V. Chernikov goto enomem; 1527e5bf684SAlexander V. Chernikov for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) { 15319e43c16SAlexander V. Chernikov struct genl_group *gg = genl_get_group(i); 15419e43c16SAlexander V. Chernikov if (gg == NULL || gg->group_family != gf) 1557e5bf684SAlexander V. Chernikov continue; 1567e5bf684SAlexander V. Chernikov 1577e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 1587e5bf684SAlexander V. Chernikov if (cmd_off == 0) 1597e5bf684SAlexander V. Chernikov goto enomem; 1607e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM); 1617e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name); 1627e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 1637e5bf684SAlexander V. Chernikov } 1647e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 1657e5bf684SAlexander V. Chernikov } 1667e5bf684SAlexander V. Chernikov if (nlmsg_end(nw)) 1677e5bf684SAlexander V. Chernikov return (0); 1687e5bf684SAlexander V. Chernikov enomem: 1697e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name); 1707e5bf684SAlexander V. Chernikov nlmsg_abort(nw); 1717e5bf684SAlexander V. Chernikov return (ENOMEM); 1727e5bf684SAlexander V. Chernikov } 1737e5bf684SAlexander V. Chernikov 1747e5bf684SAlexander V. Chernikov 1757e5bf684SAlexander V. Chernikov /* Declare ourself as a user */ 17619e43c16SAlexander V. Chernikov static void nlctrl_notify(void *arg, const struct genl_family *gf, int action); 17719e43c16SAlexander V. Chernikov static eventhandler_tag family_event_tag; 1787e5bf684SAlexander V. Chernikov 1797e5bf684SAlexander V. Chernikov static uint32_t ctrl_family_id; 1807e5bf684SAlexander V. Chernikov static uint32_t ctrl_group_id; 1817e5bf684SAlexander V. Chernikov 1827e5bf684SAlexander V. Chernikov struct nl_parsed_family { 1837e5bf684SAlexander V. Chernikov uint32_t family_id; 1847e5bf684SAlexander V. Chernikov char *family_name; 1857e5bf684SAlexander V. Chernikov uint8_t version; 1867e5bf684SAlexander V. Chernikov }; 1877e5bf684SAlexander V. Chernikov 1887e5bf684SAlexander V. Chernikov #define _IN(_field) offsetof(struct genlmsghdr, _field) 1897e5bf684SAlexander V. Chernikov #define _OUT(_field) offsetof(struct nl_parsed_family, _field) 1907e5bf684SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = { 1917e5bf684SAlexander V. Chernikov { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, 1927e5bf684SAlexander V. Chernikov }; 1937e5bf684SAlexander V. Chernikov 1947e5bf684SAlexander V. Chernikov static struct nlattr_parser nla_p_generic[] = { 195728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint16 }, 196728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_name), .cb = nlattr_get_string }, 1977e5bf684SAlexander V. Chernikov }; 1987e5bf684SAlexander V. Chernikov #undef _IN 1997e5bf684SAlexander V. Chernikov #undef _OUT 2007e5bf684SAlexander V. Chernikov NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic); 2017e5bf684SAlexander V. Chernikov 202728ca850SAlexander V. Chernikov static bool 203728ca850SAlexander V. Chernikov match_family(const struct genl_family *gf, const struct nl_parsed_family *attrs) 204728ca850SAlexander V. Chernikov { 205728ca850SAlexander V. Chernikov if (gf->family_name == NULL) 206728ca850SAlexander V. Chernikov return (false); 207728ca850SAlexander V. Chernikov if (attrs->family_id != 0 && attrs->family_id != gf->family_id) 208728ca850SAlexander V. Chernikov return (false); 209728ca850SAlexander V. Chernikov if (attrs->family_name != NULL && strcmp(attrs->family_name, gf->family_name)) 210728ca850SAlexander V. Chernikov return (false); 211728ca850SAlexander V. Chernikov return (true); 212728ca850SAlexander V. Chernikov } 213728ca850SAlexander V. Chernikov 2147e5bf684SAlexander V. Chernikov static int 2157e5bf684SAlexander V. Chernikov nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) 2167e5bf684SAlexander V. Chernikov { 2177e5bf684SAlexander V. Chernikov int error = 0; 2187e5bf684SAlexander V. Chernikov 2197e5bf684SAlexander V. Chernikov struct nl_parsed_family attrs = {}; 2207e5bf684SAlexander V. Chernikov error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs); 2217e5bf684SAlexander V. Chernikov if (error != 0) 2227e5bf684SAlexander V. Chernikov return (error); 2237e5bf684SAlexander V. Chernikov 2247e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { 2257e5bf684SAlexander V. Chernikov .cmd = CTRL_CMD_NEWFAMILY, 2267e5bf684SAlexander V. Chernikov }; 2277e5bf684SAlexander V. Chernikov 228728ca850SAlexander V. Chernikov if (attrs.family_id != 0 || attrs.family_name != NULL) { 229728ca850SAlexander V. Chernikov /* Resolve request */ 2307e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 23119e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i); 23219e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) { 233728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw); 234728ca850SAlexander V. Chernikov return (error); 235728ca850SAlexander V. Chernikov } 236728ca850SAlexander V. Chernikov } 237728ca850SAlexander V. Chernikov return (ENOENT); 238728ca850SAlexander V. Chernikov } 239728ca850SAlexander V. Chernikov 240728ca850SAlexander V. Chernikov hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI; 241728ca850SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 24219e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i); 24319e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) { 244728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw); 2457e5bf684SAlexander V. Chernikov if (error != 0) 2467e5bf684SAlexander V. Chernikov break; 2477e5bf684SAlexander V. Chernikov } 248728ca850SAlexander V. Chernikov } 249728ca850SAlexander V. Chernikov 250728ca850SAlexander V. Chernikov if (!nlmsg_end_dump(npt->nw, error, hdr)) { 251728ca850SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 252728ca850SAlexander V. Chernikov return (ENOMEM); 253728ca850SAlexander V. Chernikov } 2547e5bf684SAlexander V. Chernikov 2557e5bf684SAlexander V. Chernikov return (error); 2567e5bf684SAlexander V. Chernikov } 2577e5bf684SAlexander V. Chernikov 2587e5bf684SAlexander V. Chernikov static void 25919e43c16SAlexander V. Chernikov nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) 2607e5bf684SAlexander V. Chernikov { 2617e5bf684SAlexander V. Chernikov struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; 2627e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { .cmd = cmd }; 2637e5bf684SAlexander V. Chernikov struct nl_writer nw = {}; 2647e5bf684SAlexander V. Chernikov 2657e5bf684SAlexander V. Chernikov if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_GENERIC, ctrl_group_id)) { 2667e5bf684SAlexander V. Chernikov dump_family(&hdr, &ghdr, gf, &nw); 2677e5bf684SAlexander V. Chernikov nlmsg_flush(&nw); 2687e5bf684SAlexander V. Chernikov return; 2697e5bf684SAlexander V. Chernikov } 2707e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "error allocating group writer"); 2717e5bf684SAlexander V. Chernikov } 2727e5bf684SAlexander V. Chernikov 2737e5bf684SAlexander V. Chernikov static const struct genl_cmd nlctrl_cmds[] = { 2747e5bf684SAlexander V. Chernikov { 2757e5bf684SAlexander V. Chernikov .cmd_num = CTRL_CMD_GETFAMILY, 2767e5bf684SAlexander V. Chernikov .cmd_name = "GETFAMILY", 2777e5bf684SAlexander V. Chernikov .cmd_cb = nlctrl_handle_getfamily, 278fc47afbfSAlexander V. Chernikov .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 2797e5bf684SAlexander V. Chernikov }, 2807e5bf684SAlexander V. Chernikov }; 2817e5bf684SAlexander V. Chernikov 2827e5bf684SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; 2837e5bf684SAlexander V. Chernikov 2847e5bf684SAlexander V. Chernikov static void 28519e43c16SAlexander V. Chernikov genl_load_all(void *u __unused) 2867e5bf684SAlexander V. Chernikov { 2877e5bf684SAlexander V. Chernikov NL_VERIFY_PARSERS(all_parsers); 28819e43c16SAlexander V. Chernikov ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX); 28919e43c16SAlexander V. Chernikov genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds)); 29019e43c16SAlexander V. Chernikov ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify"); 29119e43c16SAlexander V. Chernikov family_event_tag = EVENTHANDLER_REGISTER(genl_family_event, nlctrl_notify, NULL, 29219e43c16SAlexander V. Chernikov EVENTHANDLER_PRI_ANY); 2937e5bf684SAlexander V. Chernikov netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message); 2947e5bf684SAlexander V. Chernikov } 29519e43c16SAlexander V. Chernikov SYSINIT(genl_load_all, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load_all, NULL); 2967e5bf684SAlexander V. Chernikov 2977e5bf684SAlexander V. Chernikov static void 2987e5bf684SAlexander V. Chernikov genl_unload(void *u __unused) 2997e5bf684SAlexander V. Chernikov { 30019e43c16SAlexander V. Chernikov EVENTHANDLER_DEREGISTER(genl_family_event, family_event_tag); 30119e43c16SAlexander V. Chernikov genl_unregister_family(CTRL_FAMILY_NAME); 302ab591c87SZhenlei Huang NET_EPOCH_WAIT(); 3037e5bf684SAlexander V. Chernikov } 3047e5bf684SAlexander V. Chernikov SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL); 305