The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netlink/netlink_generic.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 #include <sys/types.h>
   31 #include <sys/ck.h>
   32 #include <sys/epoch.h>
   33 #include <sys/kernel.h>
   34 #include <sys/lock.h>
   35 #include <sys/malloc.h>
   36 #include <sys/priv.h>
   37 #include <sys/socket.h>
   38 #include <sys/sx.h>
   39 
   40 #include <netlink/netlink.h>
   41 #include <netlink/netlink_ctl.h>
   42 #include <netlink/netlink_generic.h>
   43 
   44 #define DEBUG_MOD_NAME  nl_generic
   45 #define DEBUG_MAX_LEVEL LOG_DEBUG3
   46 #include <netlink/netlink_debug.h>
   47 _DECLARE_DEBUG(LOG_DEBUG3);
   48 
   49 #define MAX_FAMILIES    20
   50 #define MAX_GROUPS      64
   51 
   52 #define MIN_GROUP_NUM   48
   53 
   54 static struct sx sx_lock;
   55 
   56 #define GENL_LOCK_INIT()        sx_init(&sx_lock, "genetlink lock")
   57 #define GENL_LOCK_DESTROY()     sx_destroy(&sx_lock)
   58 #define GENL_LOCK()             sx_xlock(&sx_lock)
   59 #define GENL_UNLOCK()           sx_xunlock(&sx_lock)
   60 
   61 struct genl_family {
   62         const char      *family_name;
   63         uint16_t        family_hdrsize;
   64         uint16_t        family_id;
   65         uint16_t        family_version;
   66         uint16_t        family_attr_max;
   67         uint16_t        family_cmd_size;
   68         uint16_t        family_num_groups;
   69         struct genl_cmd *family_cmds;
   70 };
   71 
   72 static struct genl_family       families[MAX_FAMILIES];
   73 
   74 
   75 struct genl_group {
   76         struct genl_family      *group_family;
   77         const char              *group_name;
   78 };
   79 static struct genl_group        groups[MAX_GROUPS];
   80 
   81 
   82 static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr,
   83     const struct genl_family *gf, struct nl_writer *nw);
   84 static void nlctrl_notify(const struct genl_family *gf, int action);
   85 
   86 static struct genl_family *
   87 find_family(const char *family_name)
   88 {
   89         for (int i = 0; i < MAX_FAMILIES; i++) {
   90                 struct genl_family *gf = &families[i];
   91                 if (gf->family_name != NULL && !strcmp(gf->family_name, family_name))
   92                         return (gf);
   93         }
   94 
   95         return (NULL);
   96 }
   97 
   98 uint32_t
   99 genl_register_family(const char *family_name, size_t hdrsize, int family_version,
  100     int max_attr_idx)
  101 {
  102         uint32_t family_id = 0;
  103 
  104         MPASS(family_name != NULL);
  105         if (find_family(family_name) != NULL)
  106                 return (0);
  107 
  108         GENL_LOCK();
  109         for (int i = 0; i < MAX_FAMILIES; i++) {
  110                 struct genl_family *gf = &families[i];
  111                 if (gf->family_name == NULL) {
  112                         gf->family_name = family_name;
  113                         gf->family_version = family_version;
  114                         gf->family_hdrsize = hdrsize;
  115                         gf->family_attr_max = max_attr_idx;
  116                         gf->family_id = i + GENL_MIN_ID;
  117                         NL_LOG(LOG_DEBUG2, "Registered family %s id %d",
  118                             gf->family_name, gf->family_id);
  119                         family_id = gf->family_id;
  120                         nlctrl_notify(gf, CTRL_CMD_NEWFAMILY);
  121                         break;
  122                 }
  123         }
  124         GENL_UNLOCK();
  125 
  126         return (family_id);
  127 }
  128 
  129 static void
  130 free_family(struct genl_family *gf)
  131 {
  132         if (gf->family_cmds != NULL)
  133                 free(gf->family_cmds, M_NETLINK);
  134 }
  135 
  136 /*
  137  * unregister groups of a given family
  138  */
  139 static void
  140 unregister_groups(const struct genl_family *gf)
  141 {
  142 
  143         for (int i = 0; i < MAX_GROUPS; i++) {
  144                 struct genl_group *gg = &groups[i];
  145                 if (gg->group_family == gf && gg->group_name != NULL) {
  146                         gg->group_family = NULL;
  147                         gg->group_name = NULL;
  148                 }
  149         }
  150 }
  151 
  152 /*
  153  * Can sleep, I guess
  154  */
  155 bool
  156 genl_unregister_family(const char *family_name)
  157 {
  158         bool found = false;
  159 
  160         GENL_LOCK();
  161         struct genl_family *gf = find_family(family_name);
  162 
  163         nlctrl_notify(gf, CTRL_CMD_DELFAMILY);
  164 
  165         if (gf != NULL) {
  166                 found = true;
  167                 unregister_groups(gf);
  168                 /* TODO: zero pointer first */
  169                 free_family(gf);
  170                 bzero(gf, sizeof(*gf));
  171         }
  172         GENL_UNLOCK();
  173 
  174         return (found);
  175 }
  176 
  177 bool
  178 genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count)
  179 {
  180         GENL_LOCK();
  181         struct genl_family *gf = find_family(family_name);
  182         if (gf == NULL) {
  183                 GENL_UNLOCK();
  184                 return (false);
  185         }
  186 
  187         int cmd_size = gf->family_cmd_size;
  188 
  189         for (int i = 0; i < count; i++) {
  190                 MPASS(cmds[i].cmd_cb != NULL);
  191                 if (cmds[i].cmd_num >= cmd_size)
  192                         cmd_size = cmds[i].cmd_num + 1;
  193         }
  194 
  195         if (cmd_size > gf->family_cmd_size) {
  196                 /* need to realloc */
  197                 size_t sz = cmd_size * sizeof(struct genl_cmd);
  198                 void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO);
  199 
  200                 memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd));
  201                 void *old_data = gf->family_cmds;
  202                 gf->family_cmds = data;
  203                 gf->family_cmd_size = cmd_size;
  204                 free(old_data, M_NETLINK);
  205         }
  206 
  207         for (int i = 0; i < count; i++) {
  208                 const struct genl_cmd *cmd = &cmds[i];
  209                 MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL);
  210                 gf->family_cmds[cmd->cmd_num] = cmds[i];
  211                 NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s",
  212                     cmd->cmd_name, cmd->cmd_num, gf->family_name);
  213         }
  214         GENL_UNLOCK();
  215         return (true);
  216 }
  217 
  218 static struct genl_group *
  219 find_group(const struct genl_family *gf, const char *group_name)
  220 {
  221         for (int i = 0; i < MAX_GROUPS; i++) {
  222                 struct genl_group *gg = &groups[i];
  223                 if (gg->group_family == gf && !strcmp(gg->group_name, group_name))
  224                         return (gg);
  225         }
  226         return (NULL);
  227 }
  228 
  229 uint32_t
  230 genl_register_group(const char *family_name, const char *group_name)
  231 {
  232         uint32_t group_id = 0;
  233 
  234         MPASS(family_name != NULL);
  235         MPASS(group_name != NULL);
  236 
  237         GENL_LOCK();
  238         struct genl_family *gf = find_family(family_name);
  239 
  240         if (gf == NULL || find_group(gf, group_name) != NULL) {
  241                 GENL_UNLOCK();
  242                 return (0);
  243         }
  244 
  245         for (int i = 0; i < MAX_GROUPS; i++) {
  246                 struct genl_group *gg = &groups[i];
  247                 if (gg->group_family == NULL) {
  248                         gf->family_num_groups++;
  249                         gg->group_family = gf;
  250                         gg->group_name = group_name;
  251                         group_id = i + MIN_GROUP_NUM;
  252                         break;
  253                 }
  254         }
  255         GENL_UNLOCK();
  256 
  257         return (group_id);
  258 }
  259 
  260 /*
  261  * Handler called by netlink subsystem when matching netlink message is received
  262  */
  263 static int
  264 genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt)
  265 {
  266         struct nlpcb *nlp = npt->nlp;
  267         int error = 0;
  268 
  269         int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID;
  270 
  271         if (__predict_false(family_id < 0 || family_id > MAX_FAMILIES)) {
  272                 NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type);
  273                 return (ENOTSUP);
  274         }
  275 
  276         if (__predict_false(hdr->nlmsg_len < sizeof(hdr) + GENL_HDRLEN)) {
  277                 NLP_LOG(LOG_DEBUG, nlp, "invalid message size: %d", hdr->nlmsg_len);
  278                 return (EINVAL);
  279         }
  280 
  281         struct genl_family *gf = &families[family_id];
  282 
  283         struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
  284 
  285         if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) {
  286                 NLP_LOG(LOG_DEBUG, nlp, "family %s: invalid cmd %d",
  287                     gf->family_name, ghdr->cmd);
  288                 return (ENOTSUP);
  289         }
  290 
  291         struct genl_cmd *cmd = &gf->family_cmds[ghdr->cmd];
  292 
  293         if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) {
  294                 NLP_LOG(LOG_DEBUG, nlp, "family %s: cmd %d priv_check() failed",
  295                     gf->family_name, ghdr->cmd);
  296                 return (EPERM);
  297         }
  298 
  299         NLP_LOG(LOG_DEBUG2, nlp, "received family %s cmd %s(%d) len %d",
  300             gf->family_name, cmd->cmd_name, ghdr->cmd, hdr->nlmsg_len);
  301 
  302         error = cmd->cmd_cb(hdr, npt);
  303 
  304         return (error);
  305 }
  306 
  307 static uint32_t
  308 get_cmd_flags(const struct genl_cmd *cmd)
  309 {
  310         uint32_t flags = cmd->cmd_flags;
  311         if (cmd->cmd_priv != 0)
  312                 flags |= GENL_ADMIN_PERM;
  313         return (flags);
  314 }
  315 
  316 static int
  317 dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr,
  318     const struct genl_family *gf, struct nl_writer *nw)
  319 {
  320         if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
  321                 goto enomem;
  322 
  323         struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
  324         ghdr_new->cmd = ghdr->cmd;
  325         ghdr_new->version = gf->family_version;
  326         ghdr_new->reserved = 0;
  327 
  328         nlattr_add_string(nw, CTRL_ATTR_FAMILY_NAME, gf->family_name);
  329         nlattr_add_u16(nw, CTRL_ATTR_FAMILY_ID, gf->family_id);
  330         nlattr_add_u32(nw, CTRL_ATTR_VERSION, gf->family_version);
  331         nlattr_add_u32(nw, CTRL_ATTR_HDRSIZE, gf->family_hdrsize);
  332         nlattr_add_u32(nw, CTRL_ATTR_MAXATTR, gf->family_attr_max);
  333 
  334         if (gf->family_cmd_size > 0) {
  335                 int off = nlattr_add_nested(nw, CTRL_ATTR_OPS);
  336                 if (off == 0)
  337                         goto enomem;
  338                 for (int i = 0, cnt=0; i < gf->family_cmd_size; i++) {
  339                         struct genl_cmd *cmd = &gf->family_cmds[i];
  340                         if (cmd->cmd_cb == NULL)
  341                                 continue;
  342                         int cmd_off = nlattr_add_nested(nw, ++cnt);
  343                         if (cmd_off == 0)
  344                                 goto enomem;
  345 
  346                         nlattr_add_u32(nw, CTRL_ATTR_OP_ID, cmd->cmd_num);
  347                         nlattr_add_u32(nw, CTRL_ATTR_OP_FLAGS, get_cmd_flags(cmd));
  348                         nlattr_set_len(nw, cmd_off);
  349                 }
  350                 nlattr_set_len(nw, off);
  351         }
  352         if (gf->family_num_groups > 0) {
  353                 int off = nlattr_add_nested(nw, CTRL_ATTR_MCAST_GROUPS);
  354                 if (off == 0)
  355                         goto enomem;
  356                 for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) {
  357                         struct genl_group *gg = &groups[i];
  358                         if (gg->group_family != gf)
  359                                 continue;
  360 
  361                         int cmd_off = nlattr_add_nested(nw, ++cnt);
  362                         if (cmd_off == 0)
  363                                 goto enomem;
  364                         nlattr_add_u32(nw, CTRL_ATTR_MCAST_GRP_ID, i + MIN_GROUP_NUM);
  365                         nlattr_add_string(nw, CTRL_ATTR_MCAST_GRP_NAME, gg->group_name);
  366                         nlattr_set_len(nw, cmd_off);
  367                 }
  368                 nlattr_set_len(nw, off);
  369         }
  370         if (nlmsg_end(nw))
  371                 return (0);
  372 enomem:
  373         NL_LOG(LOG_DEBUG, "unable to dump family %s state (ENOMEM)", gf->family_name);
  374         nlmsg_abort(nw);
  375         return (ENOMEM);
  376 }
  377 
  378 
  379 /* Declare ourself as a user */
  380 #define CTRL_FAMILY_NAME        "nlctrl"
  381 
  382 static uint32_t ctrl_family_id;
  383 static uint32_t ctrl_group_id;
  384 
  385 struct nl_parsed_family {
  386         uint32_t        family_id;
  387         char            *family_name;
  388         uint8_t         version;
  389 };
  390 
  391 #define _IN(_field)     offsetof(struct genlmsghdr, _field)
  392 #define _OUT(_field)    offsetof(struct nl_parsed_family, _field)
  393 static const struct nlfield_parser nlf_p_generic[] = {
  394         { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
  395 };
  396 
  397 static struct nlattr_parser nla_p_generic[] = {
  398         { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = nlattr_get_uint16 },
  399         { .type = CTRL_ATTR_FAMILY_NAME , .off = _OUT(family_name), .cb = nlattr_get_string },
  400 };
  401 #undef _IN
  402 #undef _OUT
  403 NL_DECLARE_PARSER(genl_parser, struct genlmsghdr, nlf_p_generic, nla_p_generic);
  404 
  405 static bool
  406 match_family(const struct genl_family *gf, const struct nl_parsed_family *attrs)
  407 {
  408         if (gf->family_name == NULL)
  409                 return (false);
  410         if (attrs->family_id != 0 && attrs->family_id != gf->family_id)
  411                 return (false);
  412         if (attrs->family_name != NULL && strcmp(attrs->family_name, gf->family_name))
  413                 return (false);
  414         return (true);
  415 }
  416 
  417 static int
  418 nlctrl_handle_getfamily(struct nlmsghdr *hdr, struct nl_pstate *npt)
  419 {
  420         int error = 0;
  421 
  422         struct nl_parsed_family attrs = {};
  423         error = nl_parse_nlmsg(hdr, &genl_parser, npt, &attrs);
  424         if (error != 0)
  425                 return (error);
  426 
  427         struct genlmsghdr ghdr = {
  428                 .cmd = CTRL_CMD_NEWFAMILY,
  429         };
  430 
  431         if (attrs.family_id != 0 || attrs.family_name != NULL) {
  432                 /* Resolve request */
  433                 for (int i = 0; i < MAX_FAMILIES; i++) {
  434                         struct genl_family *gf = &families[i];
  435                         if (match_family(gf, &attrs)) {
  436                                 error = dump_family(hdr, &ghdr, gf, npt->nw);
  437                                 return (error);
  438                         }
  439                 }
  440                 return (ENOENT);
  441         }
  442 
  443         hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI;
  444         for (int i = 0; i < MAX_FAMILIES; i++) {
  445                 struct genl_family *gf = &families[i];
  446                 if (match_family(gf, &attrs)) {
  447                         error = dump_family(hdr, &ghdr, gf, npt->nw);
  448                         if (error != 0)
  449                                 break;
  450                 }
  451         }
  452 
  453         if (!nlmsg_end_dump(npt->nw, error, hdr)) {
  454                 NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
  455                 return (ENOMEM);
  456         }
  457 
  458         return (error);
  459 }
  460 
  461 static void
  462 nlctrl_notify(const struct genl_family *gf, int cmd)
  463 {
  464         struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC };
  465         struct genlmsghdr ghdr = { .cmd = cmd };
  466         struct nl_writer nw = {};
  467 
  468         if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_GENERIC, ctrl_group_id)) {
  469                 dump_family(&hdr, &ghdr, gf, &nw);
  470                 nlmsg_flush(&nw);
  471                 return;
  472         }
  473         NL_LOG(LOG_DEBUG, "error allocating group writer");
  474 }
  475 
  476 static const struct genl_cmd nlctrl_cmds[] = {
  477         {
  478                 .cmd_num = CTRL_CMD_GETFAMILY,
  479                 .cmd_name = "GETFAMILY",
  480                 .cmd_cb = nlctrl_handle_getfamily,
  481                 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
  482         },
  483 };
  484 
  485 static void
  486 genl_nlctrl_init(void)
  487 {
  488         ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX);
  489         genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds));
  490         ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify");
  491 }
  492 
  493 static void
  494 genl_nlctrl_destroy(void)
  495 {
  496         genl_unregister_family(CTRL_FAMILY_NAME);
  497 }
  498 
  499 static const struct nlhdr_parser *all_parsers[] = { &genl_parser };
  500 
  501 static void
  502 genl_load(void *u __unused)
  503 {
  504         GENL_LOCK_INIT();
  505         NL_VERIFY_PARSERS(all_parsers);
  506         netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message);
  507         genl_nlctrl_init();
  508 }
  509 SYSINIT(genl_load, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load, NULL);
  510 
  511 static void
  512 genl_unload(void *u __unused)
  513 {
  514         genl_nlctrl_destroy();
  515         GENL_LOCK_DESTROY();
  516         NET_EPOCH_WAIT();
  517 }
  518 SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL);

Cache object: 07cde8c2c75632f1906d0bfec2038273


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.