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 287e5bf684SAlexander V. Chernikov #include <sys/cdefs.h> 297e5bf684SAlexander V. Chernikov __FBSDID("$FreeBSD$"); 307e5bf684SAlexander V. Chernikov #include <sys/types.h> 31*fc083c3eSJung-uk Kim #include <sys/ck.h> 32*fc083c3eSJung-uk Kim #include <sys/kernel.h> 33*fc083c3eSJung-uk Kim #include <sys/lock.h> 347e5bf684SAlexander V. Chernikov #include <sys/malloc.h> 357e5bf684SAlexander V. Chernikov #include <sys/priv.h> 367e5bf684SAlexander V. Chernikov #include <sys/socket.h> 37*fc083c3eSJung-uk Kim #include <sys/sx.h> 387e5bf684SAlexander V. Chernikov 397e5bf684SAlexander V. Chernikov #include <netlink/netlink.h> 407e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 417e5bf684SAlexander V. Chernikov #include <netlink/netlink_var.h> 427e5bf684SAlexander V. Chernikov #include <netlink/netlink_generic.h> 437e5bf684SAlexander V. Chernikov 447e5bf684SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_generic 457e5bf684SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3 467e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h> 477e5bf684SAlexander V. Chernikov _DECLARE_DEBUG(LOG_DEBUG3); 487e5bf684SAlexander V. Chernikov 497e5bf684SAlexander V. Chernikov #define MAX_FAMILIES 20 507e5bf684SAlexander V. Chernikov #define MAX_GROUPS 20 517e5bf684SAlexander V. Chernikov 527e5bf684SAlexander V. Chernikov #define MIN_GROUP_NUM 48 537e5bf684SAlexander V. Chernikov 547e5bf684SAlexander V. Chernikov static struct sx sx_lock; 557e5bf684SAlexander V. Chernikov 567e5bf684SAlexander V. Chernikov #define GENL_LOCK_INIT() sx_init(&sx_lock, "genetlink lock") 577e5bf684SAlexander V. Chernikov #define GENL_LOCK_DESTROY() sx_destroy(&sx_lock) 587e5bf684SAlexander V. Chernikov #define GENL_LOCK() sx_xlock(&sx_lock) 597e5bf684SAlexander V. Chernikov #define GENL_UNLOCK() sx_xunlock(&sx_lock) 607e5bf684SAlexander V. Chernikov 617e5bf684SAlexander V. Chernikov struct genl_family { 627e5bf684SAlexander V. Chernikov const char *family_name; 637e5bf684SAlexander V. Chernikov uint16_t family_hdrsize; 647e5bf684SAlexander V. Chernikov uint16_t family_id; 657e5bf684SAlexander V. Chernikov uint16_t family_version; 667e5bf684SAlexander V. Chernikov uint16_t family_attr_max; 677e5bf684SAlexander V. Chernikov uint16_t family_cmd_size; 687e5bf684SAlexander V. Chernikov uint16_t family_num_groups; 697e5bf684SAlexander V. Chernikov struct genl_cmd *family_cmds; 707e5bf684SAlexander V. Chernikov }; 717e5bf684SAlexander V. Chernikov 727e5bf684SAlexander V. Chernikov static struct genl_family families[MAX_FAMILIES]; 737e5bf684SAlexander V. Chernikov 747e5bf684SAlexander V. Chernikov 757e5bf684SAlexander V. Chernikov struct genl_group { 767e5bf684SAlexander V. Chernikov struct genl_family *group_family; 777e5bf684SAlexander V. Chernikov const char *group_name; 787e5bf684SAlexander V. Chernikov }; 797e5bf684SAlexander V. Chernikov static struct genl_group groups[MAX_GROUPS]; 807e5bf684SAlexander V. Chernikov 817e5bf684SAlexander V. Chernikov 827e5bf684SAlexander V. Chernikov static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 837e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw); 847e5bf684SAlexander V. Chernikov static void nlctrl_notify(const struct genl_family *gf, int action); 857e5bf684SAlexander V. Chernikov 867e5bf684SAlexander V. Chernikov static struct genl_family * 877e5bf684SAlexander V. Chernikov find_family(const char *family_name) 887e5bf684SAlexander V. Chernikov { 897e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 907e5bf684SAlexander V. Chernikov struct genl_family *gf = &families[i]; 917e5bf684SAlexander V. Chernikov if (gf->family_name != NULL && !strcmp(gf->family_name, family_name)) 927e5bf684SAlexander V. Chernikov return (gf); 937e5bf684SAlexander V. Chernikov } 947e5bf684SAlexander V. Chernikov 957e5bf684SAlexander V. Chernikov return (NULL); 967e5bf684SAlexander V. Chernikov } 977e5bf684SAlexander V. Chernikov 987e5bf684SAlexander V. Chernikov uint32_t 997e5bf684SAlexander V. Chernikov genl_register_family(const char *family_name, size_t hdrsize, int family_version, 1007e5bf684SAlexander V. Chernikov int max_attr_idx) 1017e5bf684SAlexander V. Chernikov { 1027e5bf684SAlexander V. Chernikov uint32_t family_id = 0; 1037e5bf684SAlexander V. Chernikov 1047e5bf684SAlexander V. Chernikov MPASS(family_name != NULL); 1057e5bf684SAlexander V. Chernikov if (find_family(family_name) != NULL) 1067e5bf684SAlexander V. Chernikov return (0); 1077e5bf684SAlexander V. Chernikov 1087e5bf684SAlexander V. Chernikov GENL_LOCK(); 1097e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 1107e5bf684SAlexander V. Chernikov struct genl_family *gf = &families[i]; 1117e5bf684SAlexander V. Chernikov if (gf->family_name == NULL) { 1127e5bf684SAlexander V. Chernikov gf->family_name = family_name; 1137e5bf684SAlexander V. Chernikov gf->family_version = family_version; 1147e5bf684SAlexander V. Chernikov gf->family_hdrsize = hdrsize; 1157e5bf684SAlexander V. Chernikov gf->family_attr_max = max_attr_idx; 1167e5bf684SAlexander V. Chernikov gf->family_id = i + GENL_MIN_ID; 1177e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG2, "Registered family %s id %d", 1187e5bf684SAlexander V. Chernikov gf->family_name, gf->family_id); 1197e5bf684SAlexander V. Chernikov family_id = gf->family_id; 1207e5bf684SAlexander V. Chernikov nlctrl_notify(gf, CTRL_CMD_NEWFAMILY); 1217e5bf684SAlexander V. Chernikov break; 1227e5bf684SAlexander V. Chernikov } 1237e5bf684SAlexander V. Chernikov } 1247e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 1257e5bf684SAlexander V. Chernikov 1267e5bf684SAlexander V. Chernikov return (family_id); 1277e5bf684SAlexander V. Chernikov } 1287e5bf684SAlexander V. Chernikov 1297e5bf684SAlexander V. Chernikov static void 1307e5bf684SAlexander V. Chernikov free_family(struct genl_family *gf) 1317e5bf684SAlexander V. Chernikov { 1327e5bf684SAlexander V. Chernikov if (gf->family_cmds != NULL) 1337e5bf684SAlexander V. Chernikov free(gf->family_cmds, M_NETLINK); 1347e5bf684SAlexander V. Chernikov } 1357e5bf684SAlexander V. Chernikov 1367e5bf684SAlexander V. Chernikov /* 1377e5bf684SAlexander V. Chernikov * Can sleep, I guess 1387e5bf684SAlexander V. Chernikov */ 1397e5bf684SAlexander V. Chernikov bool 1407e5bf684SAlexander V. Chernikov genl_unregister_family(const char *family_name) 1417e5bf684SAlexander V. Chernikov { 1427e5bf684SAlexander V. Chernikov bool found = false; 1437e5bf684SAlexander V. Chernikov 1447e5bf684SAlexander V. Chernikov GENL_LOCK(); 1457e5bf684SAlexander V. Chernikov struct genl_family *gf = find_family(family_name); 1467e5bf684SAlexander V. Chernikov 1477e5bf684SAlexander V. Chernikov nlctrl_notify(gf, CTRL_CMD_DELFAMILY); 1487e5bf684SAlexander V. Chernikov 1497e5bf684SAlexander V. Chernikov if (gf != NULL) { 1507e5bf684SAlexander V. Chernikov found = true; 1517e5bf684SAlexander V. Chernikov /* TODO: zero pointer first */ 1527e5bf684SAlexander V. Chernikov free_family(gf); 1537e5bf684SAlexander V. Chernikov bzero(gf, sizeof(*gf)); 1547e5bf684SAlexander V. Chernikov } 1557e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 1567e5bf684SAlexander V. Chernikov 1577e5bf684SAlexander V. Chernikov return (found); 1587e5bf684SAlexander V. Chernikov } 1597e5bf684SAlexander V. Chernikov 1607e5bf684SAlexander V. Chernikov bool 1617e5bf684SAlexander V. Chernikov genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count) 1627e5bf684SAlexander V. Chernikov { 1637e5bf684SAlexander V. Chernikov GENL_LOCK(); 1647e5bf684SAlexander V. Chernikov struct genl_family *gf = find_family(family_name); 1657e5bf684SAlexander V. Chernikov if (gf == NULL) { 1667e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 1677e5bf684SAlexander V. Chernikov return (false); 1687e5bf684SAlexander V. Chernikov } 1697e5bf684SAlexander V. Chernikov 1707e5bf684SAlexander V. Chernikov int cmd_size = gf->family_cmd_size; 1717e5bf684SAlexander V. Chernikov 1727e5bf684SAlexander V. Chernikov for (int i = 0; i < count; i++) { 1737e5bf684SAlexander V. Chernikov MPASS(cmds[i].cmd_cb != NULL); 1747e5bf684SAlexander V. Chernikov if (cmds[i].cmd_num >= cmd_size) 1757e5bf684SAlexander V. Chernikov cmd_size = cmds[i].cmd_num + 1; 1767e5bf684SAlexander V. Chernikov } 1777e5bf684SAlexander V. Chernikov 1787e5bf684SAlexander V. Chernikov if (cmd_size > gf->family_cmd_size) { 1797e5bf684SAlexander V. Chernikov /* need to realloc */ 1807e5bf684SAlexander V. Chernikov size_t sz = cmd_size * sizeof(struct genl_cmd); 1817e5bf684SAlexander V. Chernikov void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO); 1827e5bf684SAlexander V. Chernikov 1837e5bf684SAlexander V. Chernikov memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd)); 1847e5bf684SAlexander V. Chernikov void *old_data = gf->family_cmds; 1857e5bf684SAlexander V. Chernikov gf->family_cmds = data; 1867e5bf684SAlexander V. Chernikov gf->family_cmd_size = cmd_size; 1877e5bf684SAlexander V. Chernikov free(old_data, M_NETLINK); 1887e5bf684SAlexander V. Chernikov } 1897e5bf684SAlexander V. Chernikov 1907e5bf684SAlexander V. Chernikov for (int i = 0; i < count; i++) { 1917e5bf684SAlexander V. Chernikov const struct genl_cmd *cmd = &cmds[i]; 1927e5bf684SAlexander V. Chernikov MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL); 1937e5bf684SAlexander V. Chernikov gf->family_cmds[cmd->cmd_num] = cmds[i]; 1947e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s", 1957e5bf684SAlexander V. Chernikov cmd->cmd_name, cmd->cmd_num, gf->family_name); 1967e5bf684SAlexander V. Chernikov } 1977e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 1987e5bf684SAlexander V. Chernikov return (true); 1997e5bf684SAlexander V. Chernikov } 2007e5bf684SAlexander V. Chernikov 2017e5bf684SAlexander V. Chernikov static struct genl_group * 2027e5bf684SAlexander V. Chernikov find_group(const struct genl_family *gf, const char *group_name) 2037e5bf684SAlexander V. Chernikov { 2047e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_GROUPS; i++) { 2057e5bf684SAlexander V. Chernikov struct genl_group *gg = &groups[i]; 2067e5bf684SAlexander V. Chernikov if (gg->group_family == gf && !strcmp(gg->group_name, group_name)) 2077e5bf684SAlexander V. Chernikov return (gg); 2087e5bf684SAlexander V. Chernikov } 2097e5bf684SAlexander V. Chernikov return (NULL); 2107e5bf684SAlexander V. Chernikov } 2117e5bf684SAlexander V. Chernikov 2127e5bf684SAlexander V. Chernikov uint32_t 2137e5bf684SAlexander V. Chernikov genl_register_group(const char *family_name, const char *group_name) 2147e5bf684SAlexander V. Chernikov { 2157e5bf684SAlexander V. Chernikov uint32_t group_id = 0; 2167e5bf684SAlexander V. Chernikov 2177e5bf684SAlexander V. Chernikov MPASS(family_name != NULL); 2187e5bf684SAlexander V. Chernikov MPASS(group_name != NULL); 2197e5bf684SAlexander V. Chernikov 2207e5bf684SAlexander V. Chernikov GENL_LOCK(); 2217e5bf684SAlexander V. Chernikov struct genl_family *gf = find_family(family_name); 2227e5bf684SAlexander V. Chernikov 2237e5bf684SAlexander V. Chernikov if (gf == NULL || find_group(gf, group_name) != NULL) { 2247e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 2257e5bf684SAlexander V. Chernikov return (0); 2267e5bf684SAlexander V. Chernikov } 2277e5bf684SAlexander V. Chernikov 2287e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_GROUPS; i++) { 2297e5bf684SAlexander V. Chernikov struct genl_group *gg = &groups[i]; 2307e5bf684SAlexander V. Chernikov if (gg->group_family == NULL) { 2317e5bf684SAlexander V. Chernikov gf->family_num_groups++; 2327e5bf684SAlexander V. Chernikov gg->group_family = gf; 2337e5bf684SAlexander V. Chernikov gg->group_name = group_name; 2347e5bf684SAlexander V. Chernikov group_id = i + MIN_GROUP_NUM; 2357e5bf684SAlexander V. Chernikov break; 2367e5bf684SAlexander V. Chernikov } 2377e5bf684SAlexander V. Chernikov } 2387e5bf684SAlexander V. Chernikov GENL_UNLOCK(); 2397e5bf684SAlexander V. Chernikov 2407e5bf684SAlexander V. Chernikov return (group_id); 2417e5bf684SAlexander V. Chernikov } 2427e5bf684SAlexander V. Chernikov 2437e5bf684SAlexander V. Chernikov /* 2447e5bf684SAlexander V. Chernikov * Handler called by netlink subsystem when matching netlink message is received 2457e5bf684SAlexander V. Chernikov */ 2467e5bf684SAlexander V. Chernikov static int 2477e5bf684SAlexander V. Chernikov genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) 2487e5bf684SAlexander V. Chernikov { 2497e5bf684SAlexander V. Chernikov struct nlpcb *nlp = npt->nlp; 2507e5bf684SAlexander V. Chernikov int error = 0; 2517e5bf684SAlexander V. Chernikov 2527e5bf684SAlexander V. Chernikov int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID; 2537e5bf684SAlexander V. Chernikov 2547e5bf684SAlexander V. Chernikov if (__predict_false(family_id < 0 || family_id > MAX_FAMILIES)) { 2557e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type); 2567e5bf684SAlexander V. Chernikov return (ENOTSUP); 2577e5bf684SAlexander V. Chernikov } 2587e5bf684SAlexander V. Chernikov 2597e5bf684SAlexander V. Chernikov if (__predict_false(hdr->nlmsg_len < sizeof(hdr) + GENL_HDRLEN)) { 2607e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len); 2617e5bf684SAlexander V. Chernikov return (EINVAL); 2627e5bf684SAlexander V. Chernikov } 2637e5bf684SAlexander V. Chernikov 2647e5bf684SAlexander V. Chernikov struct genl_family *gf = &families[family_id]; 2657e5bf684SAlexander V. Chernikov 2667e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); 2677e5bf684SAlexander V. Chernikov 2687e5bf684SAlexander V. Chernikov if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { 2697e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d", 2707e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 2717e5bf684SAlexander V. Chernikov return (ENOTSUP); 2727e5bf684SAlexander V. Chernikov } 2737e5bf684SAlexander V. Chernikov 2747e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd]; 2757e5bf684SAlexander V. Chernikov 2767e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) { 2777e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed", 2787e5bf684SAlexander V. Chernikov gf->family_name, ghdr->cmd); 2797e5bf684SAlexander V. Chernikov return (EPERM); 2807e5bf684SAlexander V. Chernikov } 2817e5bf684SAlexander V. Chernikov 2827e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d", 2837e5bf684SAlexander V. Chernikov gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len); 2847e5bf684SAlexander V. Chernikov 2857e5bf684SAlexander V. Chernikov error = cmd->cmd_cb(hdr, npt); 2867e5bf684SAlexander V. Chernikov 2877e5bf684SAlexander V. Chernikov return (error); 2887e5bf684SAlexander V. Chernikov } 2897e5bf684SAlexander V. Chernikov 2907e5bf684SAlexander V. Chernikov static uint32_t 2917e5bf684SAlexander V. Chernikov get_cmd_flags(const struct genl_cmd *cmd) 2927e5bf684SAlexander V. Chernikov { 2937e5bf684SAlexander V. Chernikov uint32_t flags = cmd->cmd_flags; 2947e5bf684SAlexander V. Chernikov if (cmd->cmd_priv != 0) 2957e5bf684SAlexander V. Chernikov flags |= GENL_ADMIN_PERM; 2967e5bf684SAlexander V. Chernikov return (flags); 2977e5bf684SAlexander V. Chernikov } 2987e5bf684SAlexander V. Chernikov 2997e5bf684SAlexander V. Chernikov static int 3007e5bf684SAlexander V. Chernikov dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, 3017e5bf684SAlexander V. Chernikov const struct genl_family *gf, struct nl_writer *nw) 3027e5bf684SAlexander V. Chernikov { 3037e5bf684SAlexander V. Chernikov if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 3047e5bf684SAlexander V. Chernikov goto enomem; 3057e5bf684SAlexander V. Chernikov 3067e5bf684SAlexander V. Chernikov struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 3077e5bf684SAlexander V. Chernikov ghdr_new->cmd = ghdr->cmd; 3087e5bf684SAlexander V. Chernikov ghdr_new->version = gf->family_version; 3097e5bf684SAlexander V. Chernikov ghdr_new->reserved = 0; 3107e5bf684SAlexander V. Chernikov 3117e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name); 3127e5bf684SAlexander V. Chernikov nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, gf->family_id); 3137e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version); 3147e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize); 3157e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max); 3167e5bf684SAlexander V. Chernikov 3177e5bf684SAlexander V. Chernikov if (gf->family_cmd_size > 0) { 3187e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_OPS); 3197e5bf684SAlexander V. Chernikov if (off == 0) 3207e5bf684SAlexander V. Chernikov goto enomem; 3217e5bf684SAlexander V. Chernikov for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) { 3227e5bf684SAlexander V. Chernikov struct genl_cmd *cmd = &gf->family_cmds[i]; 3237e5bf684SAlexander V. Chernikov if (cmd->cmd_cb == NULL) 3247e5bf684SAlexander V. Chernikov continue; 3257e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 3267e5bf684SAlexander V. Chernikov if (cmd_off == 0) 3277e5bf684SAlexander V. Chernikov goto enomem; 3287e5bf684SAlexander V. Chernikov 3297e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num); 3307e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd)); 3317e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 3327e5bf684SAlexander V. Chernikov } 3337e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 3347e5bf684SAlexander V. Chernikov } 3357e5bf684SAlexander V. Chernikov if (gf->family_num_groups > 0) { 3367e5bf684SAlexander V. Chernikov int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS); 3377e5bf684SAlexander V. Chernikov if (off == 0) 3387e5bf684SAlexander V. Chernikov goto enomem; 3397e5bf684SAlexander V. Chernikov for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) { 3407e5bf684SAlexander V. Chernikov struct genl_group *gg = &groups[i]; 3417e5bf684SAlexander V. Chernikov if (gg->group_family != gf) 3427e5bf684SAlexander V. Chernikov continue; 3437e5bf684SAlexander V. Chernikov 3447e5bf684SAlexander V. Chernikov int cmd_off = nlattr_add_nested(nw, ++cnt); 3457e5bf684SAlexander V. Chernikov if (cmd_off == 0) 3467e5bf684SAlexander V. Chernikov goto enomem; 3477e5bf684SAlexander V. Chernikov nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM); 3487e5bf684SAlexander V. Chernikov nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name); 3497e5bf684SAlexander V. Chernikov nlattr_set_len(nw, cmd_off); 3507e5bf684SAlexander V. Chernikov } 3517e5bf684SAlexander V. Chernikov nlattr_set_len(nw, off); 3527e5bf684SAlexander V. Chernikov } 3537e5bf684SAlexander V. Chernikov if (nlmsg_end(nw)) 3547e5bf684SAlexander V. Chernikov return (0); 3557e5bf684SAlexander V. Chernikov enomem: 3567e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name); 3577e5bf684SAlexander V. Chernikov nlmsg_abort(nw); 3587e5bf684SAlexander V. Chernikov return (ENOMEM); 3597e5bf684SAlexander V. Chernikov } 3607e5bf684SAlexander V. Chernikov 3617e5bf684SAlexander V. Chernikov 3627e5bf684SAlexander V. Chernikov /* Declare ourself as a user */ 3637e5bf684SAlexander V. Chernikov #define CTRL_FAMILY_NAME "nlctrl" 3647e5bf684SAlexander V. Chernikov 3657e5bf684SAlexander V. Chernikov static uint32_t ctrl_family_id; 3667e5bf684SAlexander V. Chernikov static uint32_t ctrl_group_id; 3677e5bf684SAlexander V. Chernikov 3687e5bf684SAlexander V. Chernikov struct nl_parsed_family { 3697e5bf684SAlexander V. Chernikov uint32_t family_id; 3707e5bf684SAlexander V. Chernikov char *family_name; 3717e5bf684SAlexander V. Chernikov uint8_t version; 3727e5bf684SAlexander V. Chernikov }; 3737e5bf684SAlexander V. Chernikov 3747e5bf684SAlexander V. Chernikov #define _IN(_field) offsetof(struct genlmsghdr, _field) 3757e5bf684SAlexander V. Chernikov #define _OUT(_field) offsetof(struct nl_parsed_family, _field) 3767e5bf684SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = { 3777e5bf684SAlexander V. Chernikov { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, 3787e5bf684SAlexander V. Chernikov }; 3797e5bf684SAlexander V. Chernikov 3807e5bf684SAlexander V. Chernikov static struct nlattr_parser nla_p_generic[] = { 3817e5bf684SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint32 }, 3827e5bf684SAlexander V. Chernikov { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_id), .cb = nlattr_get_string }, 3837e5bf684SAlexander V. Chernikov }; 3847e5bf684SAlexander V. Chernikov #undef _IN 3857e5bf684SAlexander V. Chernikov #undef _OUT 3867e5bf684SAlexander V. Chernikov NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic); 3877e5bf684SAlexander V. Chernikov 3887e5bf684SAlexander V. Chernikov static int 3897e5bf684SAlexander V. Chernikov nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt) 3907e5bf684SAlexander V. Chernikov { 3917e5bf684SAlexander V. Chernikov int error = 0; 3927e5bf684SAlexander V. Chernikov 3937e5bf684SAlexander V. Chernikov struct nl_parsed_family attrs = {}; 3947e5bf684SAlexander V. Chernikov error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs); 3957e5bf684SAlexander V. Chernikov if (error != 0) 3967e5bf684SAlexander V. Chernikov return (error); 3977e5bf684SAlexander V. Chernikov 3987e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { 3997e5bf684SAlexander V. Chernikov .cmd = CTRL_CMD_NEWFAMILY, 4007e5bf684SAlexander V. Chernikov }; 4017e5bf684SAlexander V. Chernikov 4027e5bf684SAlexander V. Chernikov for (int i = 0; i < MAX_FAMILIES; i++) { 4037e5bf684SAlexander V. Chernikov struct genl_family *gf = &families[i]; 4047e5bf684SAlexander V. Chernikov if (gf->family_name == NULL) 4057e5bf684SAlexander V. Chernikov continue; 4067e5bf684SAlexander V. Chernikov if (attrs.family_id != 0 && attrs.family_id != gf->family_id) 4077e5bf684SAlexander V. Chernikov continue; 4087e5bf684SAlexander V. Chernikov if (attrs.family_name != NULL && strcmp(attrs.family_name, gf->family_name)) 4097e5bf684SAlexander V. Chernikov continue; 4107e5bf684SAlexander V. Chernikov error = dump_family(hdr, &ghdr, &families[i], npt->nw); 4117e5bf684SAlexander V. Chernikov if (error != 0) 4127e5bf684SAlexander V. Chernikov break; 4137e5bf684SAlexander V. Chernikov } 4147e5bf684SAlexander V. Chernikov 4157e5bf684SAlexander V. Chernikov return (error); 4167e5bf684SAlexander V. Chernikov } 4177e5bf684SAlexander V. Chernikov 4187e5bf684SAlexander V. Chernikov static void 4197e5bf684SAlexander V. Chernikov nlctrl_notify(const struct genl_family *gf, int cmd) 4207e5bf684SAlexander V. Chernikov { 4217e5bf684SAlexander V. Chernikov struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; 4227e5bf684SAlexander V. Chernikov struct genlmsghdr ghdr = { .cmd = cmd }; 4237e5bf684SAlexander V. Chernikov struct nl_writer nw = {}; 4247e5bf684SAlexander V. Chernikov 4257e5bf684SAlexander V. Chernikov if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_GENERIC, ctrl_group_id)) { 4267e5bf684SAlexander V. Chernikov dump_family(&hdr, &ghdr, gf, &nw); 4277e5bf684SAlexander V. Chernikov nlmsg_flush(&nw); 4287e5bf684SAlexander V. Chernikov return; 4297e5bf684SAlexander V. Chernikov } 4307e5bf684SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "error allocating group writer"); 4317e5bf684SAlexander V. Chernikov } 4327e5bf684SAlexander V. Chernikov 4337e5bf684SAlexander V. Chernikov static const struct genl_cmd nlctrl_cmds[] = { 4347e5bf684SAlexander V. Chernikov { 4357e5bf684SAlexander V. Chernikov .cmd_num = CTRL_CMD_GETFAMILY, 4367e5bf684SAlexander V. Chernikov .cmd_name = "GETFAMILY", 4377e5bf684SAlexander V. Chernikov .cmd_cb = nlctrl_handle_getfamily, 4387e5bf684SAlexander V. Chernikov .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP, GENL_CMD_CAP_HASPOL, 4397e5bf684SAlexander V. Chernikov }, 4407e5bf684SAlexander V. Chernikov }; 4417e5bf684SAlexander V. Chernikov 4427e5bf684SAlexander V. Chernikov static void 4437e5bf684SAlexander V. Chernikov genl_nlctrl_init() 4447e5bf684SAlexander V. Chernikov { 4457e5bf684SAlexander V. Chernikov ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX); 4467e5bf684SAlexander V. Chernikov genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds)); 4477e5bf684SAlexander V. Chernikov ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify"); 4487e5bf684SAlexander V. Chernikov } 4497e5bf684SAlexander V. Chernikov 4507e5bf684SAlexander V. Chernikov static void 4517e5bf684SAlexander V. Chernikov genl_nlctrl_destroy() 4527e5bf684SAlexander V. Chernikov { 4537e5bf684SAlexander V. Chernikov genl_unregister_family(CTRL_FAMILY_NAME); 4547e5bf684SAlexander V. Chernikov } 4557e5bf684SAlexander V. Chernikov 4567e5bf684SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; 4577e5bf684SAlexander V. Chernikov 4587e5bf684SAlexander V. Chernikov static void 4597e5bf684SAlexander V. Chernikov genl_load(void *u __unused) 4607e5bf684SAlexander V. Chernikov { 4617e5bf684SAlexander V. Chernikov GENL_LOCK_INIT(); 4627e5bf684SAlexander V. Chernikov NL_VERIFY_PARSERS(all_parsers); 4637e5bf684SAlexander V. Chernikov netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message); 4647e5bf684SAlexander V. Chernikov genl_nlctrl_init(); 4657e5bf684SAlexander V. Chernikov } 4667e5bf684SAlexander V. Chernikov SYSINIT(genl_load, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load, NULL); 4677e5bf684SAlexander V. Chernikov 4687e5bf684SAlexander V. Chernikov static void 4697e5bf684SAlexander V. Chernikov genl_unload(void *u __unused) 4707e5bf684SAlexander V. Chernikov { 4717e5bf684SAlexander V. Chernikov genl_nlctrl_destroy(); 4727e5bf684SAlexander V. Chernikov GENL_LOCK_DESTROY(); 4737e5bf684SAlexander V. Chernikov epoch_wait_preempt(net_epoch_preempt); 4747e5bf684SAlexander V. Chernikov } 4757e5bf684SAlexander V. Chernikov SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL); 476