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
287e5bf684SAlexander V. Chernikov #include <sys/types.h>
29fc083c3eSJung-uk Kim #include <sys/ck.h>
30669d63ebSAlexander V. Chernikov #include <sys/epoch.h>
3119e43c16SAlexander V. Chernikov #include <sys/eventhandler.h>
32fc083c3eSJung-uk Kim #include <sys/kernel.h>
3304f75b98SAlexander V. Chernikov #include <sys/jail.h>
34fc083c3eSJung-uk Kim #include <sys/lock.h>
357e5bf684SAlexander V. Chernikov #include <sys/malloc.h>
367e5bf684SAlexander V. Chernikov #include <sys/priv.h>
377e5bf684SAlexander V. Chernikov #include <sys/socket.h>
38fc083c3eSJung-uk Kim #include <sys/sx.h>
397e5bf684SAlexander V. Chernikov
407e5bf684SAlexander V. Chernikov #include <netlink/netlink.h>
417e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
427e5bf684SAlexander V. Chernikov #include <netlink/netlink_generic.h>
4319e43c16SAlexander V. Chernikov #include <netlink/netlink_var.h>
447e5bf684SAlexander V. Chernikov
457e5bf684SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_generic
467e5bf684SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3
477e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h>
48fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO);
497e5bf684SAlexander V. Chernikov
507e5bf684SAlexander V. Chernikov static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr,
517e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw);
527e5bf684SAlexander V. Chernikov
537e5bf684SAlexander V. Chernikov /*
547e5bf684SAlexander V. Chernikov * Handler called by netlink subsystem when matching netlink message is received
557e5bf684SAlexander V. Chernikov */
567e5bf684SAlexander V. Chernikov static int
genl_handle_message(struct nlmsghdr * hdr,struct nl_pstate * npt)577e5bf684SAlexander V. Chernikov genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt)
587e5bf684SAlexander V. Chernikov {
597e5bf684SAlexander V. Chernikov struct nlpcb *nlp = npt->nlp;
6019e43c16SAlexander V. Chernikov struct genl_family *gf = NULL;
617e5bf684SAlexander V. Chernikov int error = 0;
627e5bf684SAlexander V. Chernikov
637e5bf684SAlexander V. Chernikov int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID;
647e5bf684SAlexander V. Chernikov
6519e43c16SAlexander V. Chernikov if (__predict_false(family_id < 0 || (gf = genl_get_family(family_id)) == NULL)) {
667e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type);
677e5bf684SAlexander V. Chernikov return (ENOTSUP);
687e5bf684SAlexander V. Chernikov }
697e5bf684SAlexander V. Chernikov
707e5bf684SAlexander V. Chernikov if (__predict_false(hdr->nlmsg_len < sizeof(hdr) + GENL_HDRLEN)) {
717e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len);
727e5bf684SAlexander V. Chernikov return (EINVAL);
737e5bf684SAlexander V. Chernikov }
747e5bf684SAlexander V. Chernikov
757e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
767e5bf684SAlexander V. Chernikov
777e5bf684SAlexander V. Chernikov if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) {
787e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d",
797e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd);
807e5bf684SAlexander V. Chernikov return (ENOTSUP);
817e5bf684SAlexander V. Chernikov }
827e5bf684SAlexander V. Chernikov
837e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd];
847e5bf684SAlexander V. Chernikov
857e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) {
867e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed",
877e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd);
887e5bf684SAlexander V. Chernikov return (EPERM);
897e5bf684SAlexander V. Chernikov }
907e5bf684SAlexander V. Chernikov
917e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d",
927e5bf684SAlexander V. Chernikov gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len);
937e5bf684SAlexander V. Chernikov
947e5bf684SAlexander V. Chernikov error = cmd->cmd_cb(hdr, npt);
957e5bf684SAlexander V. Chernikov
967e5bf684SAlexander V. Chernikov return (error);
977e5bf684SAlexander V. Chernikov }
987e5bf684SAlexander V. Chernikov
997e5bf684SAlexander V. Chernikov static uint32_t
get_cmd_flags(const struct genl_cmd * cmd)1007e5bf684SAlexander V. Chernikov get_cmd_flags(const struct genl_cmd *cmd)
1017e5bf684SAlexander V. Chernikov {
1027e5bf684SAlexander V. Chernikov uint32_t flags = cmd->cmd_flags;
1037e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0)
1047e5bf684SAlexander V. Chernikov flags |= GENL_ADMIN_PERM;
1057e5bf684SAlexander V. Chernikov return (flags);
1067e5bf684SAlexander V. Chernikov }
1077e5bf684SAlexander V. Chernikov
1087e5bf684SAlexander V. Chernikov static int
dump_family(struct nlmsghdr * hdr,struct genlmsghdr * ghdr,const struct genl_family * gf,struct nl_writer * nw)1097e5bf684SAlexander V. Chernikov dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr,
1107e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw)
1117e5bf684SAlexander V. Chernikov {
1127e5bf684SAlexander V. Chernikov if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1137e5bf684SAlexander V. Chernikov goto enomem;
1147e5bf684SAlexander V. Chernikov
1157e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1167e5bf684SAlexander V. Chernikov ghdr_new->cmd = ghdr->cmd;
1177e5bf684SAlexander V. Chernikov ghdr_new->version = gf->family_version;
1187e5bf684SAlexander V. Chernikov ghdr_new->reserved = 0;
1197e5bf684SAlexander V. Chernikov
1207e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name);
1217e5bf684SAlexander V. Chernikov nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, gf->family_id);
1227e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version);
1237e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize);
1247e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max);
1257e5bf684SAlexander V. Chernikov
1267e5bf684SAlexander V. Chernikov if (gf->family_cmd_size > 0) {
1277e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_OPS);
1287e5bf684SAlexander V. Chernikov if (off == 0)
1297e5bf684SAlexander V. Chernikov goto enomem;
1307e5bf684SAlexander V. Chernikov for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) {
1317e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[i];
1327e5bf684SAlexander V. Chernikov if (cmd->cmd_cb == NULL)
1337e5bf684SAlexander V. Chernikov continue;
1347e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt);
1357e5bf684SAlexander V. Chernikov if (cmd_off == 0)
1367e5bf684SAlexander V. Chernikov goto enomem;
1377e5bf684SAlexander V. Chernikov
1387e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num);
1397e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd));
1407e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off);
1417e5bf684SAlexander V. Chernikov }
1427e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off);
1437e5bf684SAlexander V. Chernikov }
1447e5bf684SAlexander V. Chernikov if (gf->family_num_groups > 0) {
1457e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS);
1467e5bf684SAlexander V. Chernikov if (off == 0)
1477e5bf684SAlexander V. Chernikov goto enomem;
1487e5bf684SAlexander V. Chernikov for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) {
14919e43c16SAlexander V. Chernikov struct genl_group *gg = genl_get_group(i);
15019e43c16SAlexander V. Chernikov if (gg == NULL || gg->group_family != gf)
1517e5bf684SAlexander V. Chernikov continue;
1527e5bf684SAlexander V. Chernikov
1537e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt);
1547e5bf684SAlexander V. Chernikov if (cmd_off == 0)
1557e5bf684SAlexander V. Chernikov goto enomem;
1567e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM);
1577e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name);
1587e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off);
1597e5bf684SAlexander V. Chernikov }
1607e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off);
1617e5bf684SAlexander V. Chernikov }
1627e5bf684SAlexander V. Chernikov if (nlmsg_end(nw))
1637e5bf684SAlexander V. Chernikov return (0);
1647e5bf684SAlexander V. Chernikov enomem:
1657e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name);
1667e5bf684SAlexander V. Chernikov nlmsg_abort(nw);
1677e5bf684SAlexander V. Chernikov return (ENOMEM);
1687e5bf684SAlexander V. Chernikov }
1697e5bf684SAlexander V. Chernikov
1707e5bf684SAlexander V. Chernikov
1717e5bf684SAlexander V. Chernikov /* Declare ourself as a user */
17219e43c16SAlexander V. Chernikov static void nlctrl_notify(void *arg, const struct genl_family *gf, int action);
17319e43c16SAlexander V. Chernikov static eventhandler_tag family_event_tag;
1747e5bf684SAlexander V. Chernikov
1757e5bf684SAlexander V. Chernikov static uint32_t ctrl_family_id;
1767e5bf684SAlexander V. Chernikov static uint32_t ctrl_group_id;
1777e5bf684SAlexander V. Chernikov
1787e5bf684SAlexander V. Chernikov struct nl_parsed_family {
1797e5bf684SAlexander V. Chernikov uint32_t family_id;
1807e5bf684SAlexander V. Chernikov char *family_name;
1817e5bf684SAlexander V. Chernikov uint8_t version;
1827e5bf684SAlexander V. Chernikov };
1837e5bf684SAlexander V. Chernikov
1847e5bf684SAlexander V. Chernikov #define _IN(_field) offsetof(struct genlmsghdr, _field)
1857e5bf684SAlexander V. Chernikov #define _OUT(_field) offsetof(struct nl_parsed_family, _field)
1867e5bf684SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = {
1877e5bf684SAlexander V. Chernikov { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
1887e5bf684SAlexander V. Chernikov };
1897e5bf684SAlexander V. Chernikov
1907e5bf684SAlexander V. Chernikov static struct nlattr_parser nla_p_generic[] = {
191728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint16 },
192728ca850SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_name), .cb = nlattr_get_string },
1937e5bf684SAlexander V. Chernikov };
1947e5bf684SAlexander V. Chernikov #undef _IN
1957e5bf684SAlexander V. Chernikov #undef _OUT
1967e5bf684SAlexander V. Chernikov NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic);
1977e5bf684SAlexander V. Chernikov
198728ca850SAlexander V. Chernikov static bool
match_family(const struct genl_family * gf,const struct nl_parsed_family * attrs)199728ca850SAlexander V. Chernikov match_family(const struct genl_family *gf, const struct nl_parsed_family *attrs)
200728ca850SAlexander V. Chernikov {
201728ca850SAlexander V. Chernikov if (gf->family_name == NULL)
202728ca850SAlexander V. Chernikov return (false);
203728ca850SAlexander V. Chernikov if (attrs->family_id != 0 && attrs->family_id != gf->family_id)
204728ca850SAlexander V. Chernikov return (false);
205728ca850SAlexander V. Chernikov if (attrs->family_name != NULL && strcmp(attrs->family_name, gf->family_name))
206728ca850SAlexander V. Chernikov return (false);
207728ca850SAlexander V. Chernikov return (true);
208728ca850SAlexander V. Chernikov }
209728ca850SAlexander V. Chernikov
2107e5bf684SAlexander V. Chernikov static int
nlctrl_handle_getfamily(struct nlmsghdr * hdr,struct nl_pstate * npt)2117e5bf684SAlexander V. Chernikov nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt)
2127e5bf684SAlexander V. Chernikov {
2137e5bf684SAlexander V. Chernikov int error = 0;
2147e5bf684SAlexander V. Chernikov
2157e5bf684SAlexander V. Chernikov struct nl_parsed_family attrs = {};
2167e5bf684SAlexander V. Chernikov error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs);
2177e5bf684SAlexander V. Chernikov if (error != 0)
2187e5bf684SAlexander V. Chernikov return (error);
2197e5bf684SAlexander V. Chernikov
2207e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = {
2217e5bf684SAlexander V. Chernikov .cmd = CTRL_CMD_NEWFAMILY,
2227e5bf684SAlexander V. Chernikov };
2237e5bf684SAlexander V. Chernikov
224728ca850SAlexander V. Chernikov if (attrs.family_id != 0 || attrs.family_name != NULL) {
225728ca850SAlexander V. Chernikov /* Resolve request */
2267e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) {
22719e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i);
22819e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) {
229728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw);
230728ca850SAlexander V. Chernikov return (error);
231728ca850SAlexander V. Chernikov }
232728ca850SAlexander V. Chernikov }
233728ca850SAlexander V. Chernikov return (ENOENT);
234728ca850SAlexander V. Chernikov }
235728ca850SAlexander V. Chernikov
236728ca850SAlexander V. Chernikov hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI;
237728ca850SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) {
23819e43c16SAlexander V. Chernikov struct genl_family *gf = genl_get_family(i);
23919e43c16SAlexander V. Chernikov if (gf != NULL && match_family(gf, &attrs)) {
240728ca850SAlexander V. Chernikov error = dump_family(hdr, &ghdr, gf, npt->nw);
2417e5bf684SAlexander V. Chernikov if (error != 0)
2427e5bf684SAlexander V. Chernikov break;
2437e5bf684SAlexander V. Chernikov }
244728ca850SAlexander V. Chernikov }
245728ca850SAlexander V. Chernikov
246728ca850SAlexander V. Chernikov if (!nlmsg_end_dump(npt->nw, error, hdr)) {
247728ca850SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
248728ca850SAlexander V. Chernikov return (ENOMEM);
249728ca850SAlexander V. Chernikov }
2507e5bf684SAlexander V. Chernikov
2517e5bf684SAlexander V. Chernikov return (error);
2527e5bf684SAlexander V. Chernikov }
2537e5bf684SAlexander V. Chernikov
2547e5bf684SAlexander V. Chernikov static void
nlctrl_notify(void * arg __unused,const struct genl_family * gf,int cmd)25519e43c16SAlexander V. Chernikov nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd)
2567e5bf684SAlexander V. Chernikov {
2577e5bf684SAlexander V. Chernikov struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC };
2587e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { .cmd = cmd };
2597e5bf684SAlexander V. Chernikov struct nl_writer nw = {};
2607e5bf684SAlexander V. Chernikov
2617e5bf684SAlexander V. Chernikov if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_GENERIC, ctrl_group_id)) {
2627e5bf684SAlexander V. Chernikov dump_family(&hdr, &ghdr, gf, &nw);
2637e5bf684SAlexander V. Chernikov nlmsg_flush(&nw);
2647e5bf684SAlexander V. Chernikov return;
2657e5bf684SAlexander V. Chernikov }
2667e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "error allocating group writer");
2677e5bf684SAlexander V. Chernikov }
2687e5bf684SAlexander V. Chernikov
2697e5bf684SAlexander V. Chernikov static const struct genl_cmd nlctrl_cmds[] = {
2707e5bf684SAlexander V. Chernikov {
2717e5bf684SAlexander V. Chernikov .cmd_num = CTRL_CMD_GETFAMILY,
2727e5bf684SAlexander V. Chernikov .cmd_name = "GETFAMILY",
2737e5bf684SAlexander V. Chernikov .cmd_cb = nlctrl_handle_getfamily,
274fc47afbfSAlexander V. Chernikov .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
2757e5bf684SAlexander V. Chernikov },
2767e5bf684SAlexander V. Chernikov };
2777e5bf684SAlexander V. Chernikov
2787e5bf684SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &genl_parser };
2797e5bf684SAlexander V. Chernikov
2807e5bf684SAlexander V. Chernikov static void
genl_load_all(void * u __unused)28119e43c16SAlexander V. Chernikov genl_load_all(void *u __unused)
2827e5bf684SAlexander V. Chernikov {
2837e5bf684SAlexander V. Chernikov NL_VERIFY_PARSERS(all_parsers);
28419e43c16SAlexander V. Chernikov ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX);
28519e43c16SAlexander V. Chernikov genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds));
28619e43c16SAlexander V. Chernikov ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify");
28719e43c16SAlexander V. Chernikov family_event_tag = EVENTHANDLER_REGISTER(genl_family_event, nlctrl_notify, NULL,
28819e43c16SAlexander V. Chernikov EVENTHANDLER_PRI_ANY);
2897e5bf684SAlexander V. Chernikov netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message);
2907e5bf684SAlexander V. Chernikov }
29119e43c16SAlexander V. Chernikov SYSINIT(genl_load_all, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load_all, NULL);
2927e5bf684SAlexander V. Chernikov
2937e5bf684SAlexander V. Chernikov static void
genl_unload(void * u __unused)2947e5bf684SAlexander V. Chernikov genl_unload(void *u __unused)
2957e5bf684SAlexander V. Chernikov {
296*4bdf7f69SLin Ma netlink_unregister_proto(NETLINK_GENERIC);
29719e43c16SAlexander V. Chernikov EVENTHANDLER_DEREGISTER(genl_family_event, family_event_tag);
29819e43c16SAlexander V. Chernikov genl_unregister_family(CTRL_FAMILY_NAME);
299ab591c87SZhenlei Huang NET_EPOCH_WAIT();
3007e5bf684SAlexander V. Chernikov }
3017e5bf684SAlexander V. Chernikov SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL);
302