17e5bf684SAlexander V. Chernikov /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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 #include <sys/types.h> 32fc083c3eSJung-uk Kim #include <sys/ck.h> 33669d63ebSAlexander V. Chernikov #include <sys/epoch.h> 3419e43c16SAlexander V. Chernikov #include <sys/eventhandler.h> 35fc083c3eSJung-uk Kim #include <sys/kernel.h> 3604f75b98SAlexander V. Chernikov #include <sys/jail.h> 37fc083c3eSJung-uk Kim #include <sys/lock.h> 387e5bf684SAlexander V. Chernikov #include <sys/malloc.h> 397e5bf684SAlexander V. Chernikov #include <sys/priv.h> 407e5bf684SAlexander V. Chernikov #include <sys/socket.h> 41fc083c3eSJung-uk Kim #include <sys/sx.h> 427e5bf684SAlexander V. Chernikov 437e5bf684SAlexander V. Chernikov #include <netlink/netlink.h> 447e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 457e5bf684SAlexander V. Chernikov #include <netlink/netlink_generic.h> 4619e43c16SAlexander V. Chernikov #include <netlink/netlink_var.h> 477e5bf684SAlexander V. Chernikov 487e5bf684SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_generic 497e5bf684SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3 507e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h> 51fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO); 527e5bf684SAlexander V. Chernikov 537e5bf684SAlexander V. Chernikov static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 547e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw); 557e5bf684SAlexander V. Chernikov 567e5bf684SAlexander V. Chernikov /* 577e5bf684SAlexander V. Chernikov * Handler called by netlink subsystem when matching netlink message is received 587e5bf684SAlexander V. Chernikov */ 597e5bf684SAlexander V. Chernikov static int 607e5bf684SAlexander V. Chernikov genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) 617e5bf684SAlexander V. Chernikov { 627e5bf684SAlexander V. Chernikov struct nlpcb *nlp = npt->nlp; 6319e43c16SAlexander V. Chernikov struct genl_family *gf = NULL; 647e5bf684SAlexander V. Chernikov int error = 0; 657e5bf684SAlexander V. Chernikov 667e5bf684SAlexander V. Chernikov int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID; 677e5bf684SAlexander V. Chernikov 6819e43c16SAlexander V. Chernikov if (__predict_false(family_id < 0 || (gf = genl_get_family(family_id)) == NULL)) { 697e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type); 707e5bf684SAlexander V. Chernikov return (ENOTSUP); 717e5bf684SAlexander V. Chernikov } 727e5bf684SAlexander V. Chernikov 737e5bf684SAlexander V. Chernikov if (__predict_false(hdr->nlmsg_len < sizeof(hdr) + GENL_HDRLEN)) { 747e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len); 757e5bf684SAlexander V. Chernikov return (EINVAL); 767e5bf684SAlexander V. Chernikov } 777e5bf684SAlexander V. Chernikov 787e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); 797e5bf684SAlexander V. Chernikov 807e5bf684SAlexander V. Chernikov if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { 817e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d", 827e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 837e5bf684SAlexander V. Chernikov return (ENOTSUP); 847e5bf684SAlexander V. Chernikov } 857e5bf684SAlexander V. Chernikov 867e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd]; 877e5bf684SAlexander V. Chernikov 887e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) { 897e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed", 907e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 917e5bf684SAlexander V. Chernikov return (EPERM); 927e5bf684SAlexander V. Chernikov } 937e5bf684SAlexander V. Chernikov 947e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d", 957e5bf684SAlexander V. Chernikov gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len); 967e5bf684SAlexander V. Chernikov 977e5bf684SAlexander V. Chernikov error = cmd->cmd_cb(hdr, npt); 987e5bf684SAlexander V. Chernikov 997e5bf684SAlexander V. Chernikov return (error); 1007e5bf684SAlexander V. Chernikov } 1017e5bf684SAlexander V. Chernikov 1027e5bf684SAlexander V. Chernikov static uint32_t 1037e5bf684SAlexander V. Chernikov get_cmd_flags(const struct genl_cmd *cmd) 1047e5bf684SAlexander V. Chernikov { 1057e5bf684SAlexander V. Chernikov uint32_t flags = cmd->cmd_flags; 1067e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0) 1077e5bf684SAlexander V. Chernikov flags |= GENL_ADMIN_PERM; 1087e5bf684SAlexander V. Chernikov return (flags); 1097e5bf684SAlexander V. Chernikov } 1107e5bf684SAlexander V. Chernikov 1117e5bf684SAlexander V. Chernikov static int 1127e5bf684SAlexander V. Chernikov dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 1137e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw) 1147e5bf684SAlexander V. Chernikov { 1157e5bf684SAlexander V. Chernikov if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 1167e5bf684SAlexander V. Chernikov goto enomem; 1177e5bf684SAlexander V. Chernikov 1187e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 1197e5bf684SAlexander V. Chernikov ghdr_new->cmd = ghdr->cmd; 1207e5bf684SAlexander V. Chernikov ghdr_new->version = gf->family_version; 1217e5bf684SAlexander V. Chernikov ghdr_new->reserved = 0; 1227e5bf684SAlexander V. Chernikov 1237e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name); 1247e5bf684SAlexander V. Chernikov nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, gf->family_id); 1257e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version); 1267e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize); 1277e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max); 1287e5bf684SAlexander V. Chernikov 1297e5bf684SAlexander V. Chernikov if (gf->family_cmd_size > 0) { 1307e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_OPS); 1317e5bf684SAlexander V. Chernikov if (off == 0) 1327e5bf684SAlexander V. Chernikov goto enomem; 1337e5bf684SAlexander V. Chernikov for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) { 1347e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[i]; 1357e5bf684SAlexander V. Chernikov if (cmd->cmd_cb == NULL) 1367e5bf684SAlexander V. Chernikov continue; 1377e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 1387e5bf684SAlexander V. Chernikov if (cmd_off == 0) 1397e5bf684SAlexander V. Chernikov goto enomem; 1407e5bf684SAlexander V. Chernikov 1417e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num); 1427e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd)); 1437e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 1447e5bf684SAlexander V. Chernikov } 1457e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 1467e5bf684SAlexander V. Chernikov } 1477e5bf684SAlexander V. Chernikov if (gf->family_num_groups > 0) { 1487e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS); 1497e5bf684SAlexander V. Chernikov if (off == 0) 1507e5bf684SAlexander V. Chernikov goto enomem; 1517e5bf684SAlexander V. Chernikov for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) { 15219e43c16SAlexander V. Chernikov struct genl_group *gg = genl_get_group(i); 15319e43c16SAlexander V. Chernikov if (gg == NULL || gg->group_family != gf) 1547e5bf684SAlexander V. Chernikov continue; 1557e5bf684SAlexander V. Chernikov 1567e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 1577e5bf684SAlexander V. Chernikov if (cmd_off == 0) 1587e5bf684SAlexander V. Chernikov goto enomem; 1597e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM); 1607e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name); 1617e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 1627e5bf684SAlexander V. Chernikov } 1637e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 1647e5bf684SAlexander V. Chernikov } 1657e5bf684SAlexander V. Chernikov if (nlmsg_end(nw)) 1667e5bf684SAlexander V. Chernikov return (0); 1677e5bf684SAlexander V. Chernikov enomem: 1687e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name); 1697e5bf684SAlexander V. Chernikov nlmsg_abort(nw); 1707e5bf684SAlexander V. Chernikov return (ENOMEM); 1717e5bf684SAlexander V. Chernikov } 1727e5bf684SAlexander V. Chernikov 1737e5bf684SAlexander V. Chernikov 1747e5bf684SAlexander V. Chernikov /* Declare ourself as a user */ 17519e43c16SAlexander V. Chernikov static void nlctrl_notify(void *arg, const struct genl_family *gf, int action); 17619e43c16SAlexander V. Chernikov static eventhandler_tag family_event_tag; 1777e5bf684SAlexander V. Chernikov 1787e5bf684SAlexander V. Chernikov static uint32_t ctrl_family_id; 1797e5bf684SAlexander V. Chernikov static uint32_t ctrl_group_id; 1807e5bf684SAlexander V. Chernikov 1817e5bf684SAlexander V. Chernikov struct nl_parsed_family { 1827e5bf684SAlexander V. Chernikov uint32_t family_id; 1837e5bf684SAlexander V. Chernikov char *family_name; 1847e5bf684SAlexander V. Chernikov uint8_t version; 1857e5bf684SAlexander V. Chernikov }; 1867e5bf684SAlexander V. Chernikov 1877e5bf684SAlexander V. Chernikov #define _IN(_field) offsetof(struct genlmsghdr, _field) 1887e5bf684SAlexander V. Chernikov #define _OUT(_field) offsetof(struct nl_parsed_family, _field) 1897e5bf684SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = { 1907e5bf684SAlexander V. Chernikov { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, 1917e5bf684SAlexander V. Chernikov }; 1927e5bf684SAlexander V. Chernikov 1937e5bf684SAlexander V. Chernikov static struct nlattr_parser nla_p_generic[] = { 194728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint16 }, 195728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_name), .cb = nlattr_get_string }, 1967e5bf684SAlexander V. Chernikov }; 1977e5bf684SAlexander V. Chernikov #undef _IN 1987e5bf684SAlexander V. Chernikov #undef _OUT 1997e5bf684SAlexander V. Chernikov NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic); 2007e5bf684SAlexander V. Chernikov 201728ca850SAlexander V. Chernikov static bool 202728ca850SAlexander V. Chernikov match_family(const struct genl_family *gf, const struct nl_parsed_family *attrs) 203728ca850SAlexander V. Chernikov { 204728ca850SAlexander V. Chernikov if (gf->family_name == NULL) 205728ca850SAlexander V. Chernikov return (false); 206728ca850SAlexander V. Chernikov if (attrs->family_id != 0 && attrs->family_id != gf->family_id) 207728ca850SAlexander V. Chernikov return (false); 208728ca850SAlexander V. Chernikov if (attrs->family_name != NULL && strcmp(attrs->family_name, gf->family_name)) 209728ca850SAlexander V. Chernikov return (false); 210728ca850SAlexander V. Chernikov return (true); 211728ca850SAlexander V. Chernikov } 212728ca850SAlexander V. Chernikov 2137e5bf684SAlexander V. Chernikov static int 2147e5bf684SAlexander V. Chernikov nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) 2157e5bf684SAlexander V. Chernikov { 2167e5bf684SAlexander V. Chernikov int error = 0; 2177e5bf684SAlexander V. Chernikov 2187e5bf684SAlexander V. Chernikov struct nl_parsed_family attrs = {}; 2197e5bf684SAlexander V. Chernikov error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs); 2207e5bf684SAlexander V. Chernikov if (error != 0) 2217e5bf684SAlexander V. Chernikov return (error); 2227e5bf684SAlexander V. Chernikov 2237e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { 2247e5bf684SAlexander V. Chernikov .cmd = CTRL_CMD_NEWFAMILY, 2257e5bf684SAlexander V. Chernikov }; 2267e5bf684SAlexander V. Chernikov 227728ca850SAlexander V. Chernikov if (attrs.family_id != 0 || attrs.family_name != NULL) { 228728ca850SAlexander V. Chernikov /* Resolve request */ 2297e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 23019e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i); 23119e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) { 232728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw); 233728ca850SAlexander V. Chernikov return (error); 234728ca850SAlexander V. Chernikov } 235728ca850SAlexander V. Chernikov } 236728ca850SAlexander V. Chernikov return (ENOENT); 237728ca850SAlexander V. Chernikov } 238728ca850SAlexander V. Chernikov 239728ca850SAlexander V. Chernikov hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI; 240728ca850SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 24119e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i); 24219e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) { 243728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw); 2447e5bf684SAlexander V. Chernikov if (error != 0) 2457e5bf684SAlexander V. Chernikov break; 2467e5bf684SAlexander V. Chernikov } 247728ca850SAlexander V. Chernikov } 248728ca850SAlexander V. Chernikov 249728ca850SAlexander V. Chernikov if (!nlmsg_end_dump(npt->nw, error, hdr)) { 250728ca850SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 251728ca850SAlexander V. Chernikov return (ENOMEM); 252728ca850SAlexander V. Chernikov } 2537e5bf684SAlexander V. Chernikov 2547e5bf684SAlexander V. Chernikov return (error); 2557e5bf684SAlexander V. Chernikov } 2567e5bf684SAlexander V. Chernikov 2577e5bf684SAlexander V. Chernikov static void 25819e43c16SAlexander V. Chernikov nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) 2597e5bf684SAlexander V. Chernikov { 2607e5bf684SAlexander V. Chernikov struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; 2617e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { .cmd = cmd }; 2627e5bf684SAlexander V. Chernikov struct nl_writer nw = {}; 2637e5bf684SAlexander V. Chernikov 2647e5bf684SAlexander V. Chernikov if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_GENERIC, ctrl_group_id)) { 2657e5bf684SAlexander V. Chernikov dump_family(&hdr, &ghdr, gf, &nw); 2667e5bf684SAlexander V. Chernikov nlmsg_flush(&nw); 2677e5bf684SAlexander V. Chernikov return; 2687e5bf684SAlexander V. Chernikov } 2697e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "error allocating group writer"); 2707e5bf684SAlexander V. Chernikov } 2717e5bf684SAlexander V. Chernikov 2727e5bf684SAlexander V. Chernikov static const struct genl_cmd nlctrl_cmds[] = { 2737e5bf684SAlexander V. Chernikov { 2747e5bf684SAlexander V. Chernikov .cmd_num = CTRL_CMD_GETFAMILY, 2757e5bf684SAlexander V. Chernikov .cmd_name = "GETFAMILY", 2767e5bf684SAlexander V. Chernikov .cmd_cb = nlctrl_handle_getfamily, 277fc47afbfSAlexander V. Chernikov .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 2787e5bf684SAlexander V. Chernikov }, 2797e5bf684SAlexander V. Chernikov }; 2807e5bf684SAlexander V. Chernikov 2817e5bf684SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; 2827e5bf684SAlexander V. Chernikov 2837e5bf684SAlexander V. Chernikov static void 28419e43c16SAlexander V. Chernikov genl_load_all(void *u __unused) 2857e5bf684SAlexander V. Chernikov { 2867e5bf684SAlexander V. Chernikov NL_VERIFY_PARSERS(all_parsers); 28719e43c16SAlexander V. Chernikov ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX); 28819e43c16SAlexander V. Chernikov genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds)); 28919e43c16SAlexander V. Chernikov ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify"); 29019e43c16SAlexander V. Chernikov family_event_tag = EVENTHANDLER_REGISTER(genl_family_event, nlctrl_notify, NULL, 29119e43c16SAlexander V. Chernikov EVENTHANDLER_PRI_ANY); 2927e5bf684SAlexander V. Chernikov netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message); 2937e5bf684SAlexander V. Chernikov } 29419e43c16SAlexander V. Chernikov SYSINIT(genl_load_all, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load_all, NULL); 2957e5bf684SAlexander V. Chernikov 2967e5bf684SAlexander V. Chernikov static void 2977e5bf684SAlexander V. Chernikov genl_unload(void *u __unused) 2987e5bf684SAlexander V. Chernikov { 299*4bdf7f69SLin Ma netlink_unregister_proto(NETLINK_GENERIC); 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