diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -4424,10 +4424,12 @@ netipsec/xform_tcp.c optional ipsec inet tcp_signature | \ ipsec inet6 tcp_signature | ipsec_support inet tcp_signature | \ ipsec_support inet6 tcp_signature +netlink/netlink_generic_kpi.c standard +netlink/netlink_glue.c standard +netlink/netlink_message_parser.c standard netlink/netlink_domain.c optional netlink netlink/netlink_generic.c optional netlink netlink/netlink_io.c optional netlink -netlink/netlink_message_parser.c optional netlink netlink/netlink_message_writer.c optional netlink netlink/netlink_module.c optional netlink netlink/netlink_route.c optional netlink diff --git a/sys/conf/options b/sys/conf/options --- a/sys/conf/options +++ b/sys/conf/options @@ -463,7 +463,7 @@ MBUF_STRESS_TEST MROUTING opt_mrouting.h NFSLOCKD -NETLINK +NETLINK opt_netlink.h PF_DEFAULT_TO_DROP opt_pf.h ROUTE_MPATH opt_route.h ROUTETABLES opt_route.h diff --git a/sys/modules/netlink/Makefile b/sys/modules/netlink/Makefile --- a/sys/modules/netlink/Makefile +++ b/sys/modules/netlink/Makefile @@ -2,10 +2,12 @@ KMOD= netlink SRCS = netlink_module.c netlink_domain.c netlink_io.c \ - netlink_message_parser.c netlink_message_writer.c netlink_generic.c \ + netlink_message_writer.c netlink_generic.c \ netlink_route.c route/iface.c route/iface_drivers.c route/neigh.c \ route/nexthop.c route/rt.c -SRCS+= opt_inet.h opt_inet6.h opt_route.h +SRCS+= opt_inet.h opt_inet6.h opt_route.h opt_netlink.h + +CFLAGS+= -DNETLINK_MODULE EXPORT_SYMS= EXPORT_SYMS+= nlmsg_get_chain_writer diff --git a/sys/net/route.c b/sys/net/route.c --- a/sys/net/route.c +++ b/sys/net/route.c @@ -703,22 +703,3 @@ netlink_callback_p->ifmsg_f(ifp, if_flags_mask); } -/* Netlink-related callbacks needed to glue rtsock, netlink and linuxolator */ -static void -ignore_route_event(uint32_t fibnum, const struct rib_cmd_info *rc) -{ -} - -static void -ignore_ifmsg_event(struct ifnet *ifp, int if_flags_mask) -{ -} - -static struct rtbridge ignore_cb = { - .route_f = ignore_route_event, - .ifmsg_f = ignore_ifmsg_event, -}; - -void *linux_netlink_p = NULL; /* Callback pointer for Linux translator functions */ -struct rtbridge *rtsock_callback_p = &ignore_cb; -struct rtbridge *netlink_callback_p = &ignore_cb; diff --git a/sys/netlink/netlink_ctl.h b/sys/netlink/netlink_ctl.h --- a/sys/netlink/netlink_ctl.h +++ b/sys/netlink/netlink_ctl.h @@ -33,6 +33,8 @@ * This file provides headers for the public KPI of the netlink * subsystem */ +#include + MALLOC_DECLARE(M_NETLINK); @@ -81,6 +83,7 @@ bool nl_has_listeners(int netlink_family, uint32_t groups_mask); bool nlp_has_priv(struct nlpcb *nlp, int priv); struct ucred *nlp_get_cred(struct nlpcb *nlp); +uint32_t nlp_get_pid(const struct nlpcb *nlp); /* netlink_generic.c */ struct genl_cmd { @@ -98,8 +101,12 @@ int count); uint32_t genl_register_group(const char *family_name, const char *group_name); -/* Debug */ -uint32_t nlp_get_pid(const struct nlpcb *nlp); +struct genl_family; +const char *genl_get_family_name(const struct genl_family *gf); +uint32_t genl_get_family_id(const struct genl_family *gf); + +typedef void (*genl_family_event_handler_t)(void *arg, const struct genl_family *gf, int action); +EVENTHANDLER_DECLARE(genl_family_event, genl_family_event_handler_t); #endif #endif diff --git a/sys/netlink/netlink_domain.c b/sys/netlink/netlink_domain.c --- a/sys/netlink/netlink_domain.c +++ b/sys/netlink/netlink_domain.c @@ -83,12 +83,6 @@ sysctl_handle_nl_maxsockbuf, "LU", "Maximum Netlink socket buffer size"); -uint32_t -nlp_get_pid(const struct nlpcb *nlp) -{ - return (nlp->nl_process_id); -} - /* * Looks up a nlpcb struct based on the @portid. Need to claim nlsock_mtx. * Returns nlpcb pointer if present else NULL @@ -206,18 +200,6 @@ return (V_nl_ctl != NULL); } -bool -nlp_has_priv(struct nlpcb *nlp, int priv) -{ - return (priv_check_cred(nlp->nl_cred, priv) == 0); -} - -struct ucred * -nlp_get_cred(struct nlpcb *nlp) -{ - return (nlp->nl_cred); -} - static uint32_t nl_find_port(void) { diff --git a/sys/netlink/netlink_generic.c b/sys/netlink/netlink_generic.c --- a/sys/netlink/netlink_generic.c +++ b/sys/netlink/netlink_generic.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -40,221 +41,15 @@ #include #include #include +#include #define DEBUG_MOD_NAME nl_generic #define DEBUG_MAX_LEVEL LOG_DEBUG3 #include _DECLARE_DEBUG(LOG_DEBUG3); -#define MAX_FAMILIES 20 -#define MAX_GROUPS 64 - -#define MIN_GROUP_NUM 48 - -static struct sx sx_lock; - -#define GENL_LOCK_INIT() sx_init(&sx_lock, "genetlink lock") -#define GENL_LOCK_DESTROY() sx_destroy(&sx_lock) -#define GENL_LOCK() sx_xlock(&sx_lock) -#define GENL_UNLOCK() sx_xunlock(&sx_lock) - -struct genl_family { - const char *family_name; - uint16_t family_hdrsize; - uint16_t family_id; - uint16_t family_version; - uint16_t family_attr_max; - uint16_t family_cmd_size; - uint16_t family_num_groups; - struct genl_cmd *family_cmds; -}; - -static struct genl_family families[MAX_FAMILIES]; - - -struct genl_group { - struct genl_family *group_family; - const char *group_name; -}; -static struct genl_group groups[MAX_GROUPS]; - - static int dump_family(struct nlmsghdr *hdr, struct genlmsghdr *ghdr, const struct genl_family *gf, struct nl_writer *nw); -static void nlctrl_notify(const struct genl_family *gf, int action); - -static struct genl_family * -find_family(const char *family_name) -{ - for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = &families[i]; - if (gf->family_name != NULL && !strcmp(gf->family_name, family_name)) - return (gf); - } - - return (NULL); -} - -uint32_t -genl_register_family(const char *family_name, size_t hdrsize, int family_version, - int max_attr_idx) -{ - uint32_t family_id = 0; - - MPASS(family_name != NULL); - if (find_family(family_name) != NULL) - return (0); - - GENL_LOCK(); - for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = &families[i]; - if (gf->family_name == NULL) { - gf->family_name = family_name; - gf->family_version = family_version; - gf->family_hdrsize = hdrsize; - gf->family_attr_max = max_attr_idx; - gf->family_id = i + GENL_MIN_ID; - NL_LOG(LOG_DEBUG2, "Registered family %s id %d", - gf->family_name, gf->family_id); - family_id = gf->family_id; - nlctrl_notify(gf, CTRL_CMD_NEWFAMILY); - break; - } - } - GENL_UNLOCK(); - - return (family_id); -} - -static void -free_family(struct genl_family *gf) -{ - if (gf->family_cmds != NULL) - free(gf->family_cmds, M_NETLINK); -} - -/* - * unregister groups of a given family - */ -static void -unregister_groups(const struct genl_family *gf) -{ - - for (int i = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = &groups[i]; - if (gg->group_family == gf && gg->group_name != NULL) { - gg->group_family = NULL; - gg->group_name = NULL; - } - } -} - -/* - * Can sleep, I guess - */ -bool -genl_unregister_family(const char *family_name) -{ - bool found = false; - - GENL_LOCK(); - struct genl_family *gf = find_family(family_name); - - if (gf != NULL) { - nlctrl_notify(gf, CTRL_CMD_DELFAMILY); - found = true; - unregister_groups(gf); - /* TODO: zero pointer first */ - free_family(gf); - bzero(gf, sizeof(*gf)); - } - GENL_UNLOCK(); - - return (found); -} - -bool -genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count) -{ - GENL_LOCK(); - struct genl_family *gf = find_family(family_name); - if (gf == NULL) { - GENL_UNLOCK(); - return (false); - } - - int cmd_size = gf->family_cmd_size; - - for (int i = 0; i < count; i++) { - MPASS(cmds[i].cmd_cb != NULL); - if (cmds[i].cmd_num >= cmd_size) - cmd_size = cmds[i].cmd_num + 1; - } - - if (cmd_size > gf->family_cmd_size) { - /* need to realloc */ - size_t sz = cmd_size * sizeof(struct genl_cmd); - void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO); - - memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd)); - void *old_data = gf->family_cmds; - gf->family_cmds = data; - gf->family_cmd_size = cmd_size; - free(old_data, M_NETLINK); - } - - for (int i = 0; i < count; i++) { - const struct genl_cmd *cmd = &cmds[i]; - MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL); - gf->family_cmds[cmd->cmd_num] = cmds[i]; - NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s", - cmd->cmd_name, cmd->cmd_num, gf->family_name); - } - GENL_UNLOCK(); - return (true); -} - -static struct genl_group * -find_group(const struct genl_family *gf, const char *group_name) -{ - for (int i = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = &groups[i]; - if (gg->group_family == gf && !strcmp(gg->group_name, group_name)) - return (gg); - } - return (NULL); -} - -uint32_t -genl_register_group(const char *family_name, const char *group_name) -{ - uint32_t group_id = 0; - - MPASS(family_name != NULL); - MPASS(group_name != NULL); - - GENL_LOCK(); - struct genl_family *gf = find_family(family_name); - - if (gf == NULL || find_group(gf, group_name) != NULL) { - GENL_UNLOCK(); - return (0); - } - - for (int i = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = &groups[i]; - if (gg->group_family == NULL) { - gf->family_num_groups++; - gg->group_family = gf; - gg->group_name = group_name; - group_id = i + MIN_GROUP_NUM; - break; - } - } - GENL_UNLOCK(); - - return (group_id); -} /* * Handler called by netlink subsystem when matching netlink message is received @@ -263,11 +58,12 @@ genl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) { struct nlpcb *nlp = npt->nlp; + struct genl_family *gf = NULL; int error = 0; int family_id = (int)hdr->nlmsg_type - GENL_MIN_ID; - if (__predict_false(family_id < 0 || family_id >= MAX_FAMILIES)) { + if (__predict_false(family_id < 0 || (gf = genl_get_family(family_id)) == NULL)) { NLP_LOG(LOG_DEBUG, nlp, "invalid message type: %d", hdr->nlmsg_type); return (ENOTSUP); } @@ -277,8 +73,6 @@ return (EINVAL); } - struct genl_family *gf = &families[family_id]; - struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1); if (ghdr->cmd >= gf->family_cmd_size || gf->family_cmds[ghdr->cmd].cmd_cb == NULL) { @@ -353,8 +147,8 @@ if (off == 0) goto enomem; for (int i = 0, cnt = 0; i < MAX_GROUPS; i++) { - struct genl_group *gg = &groups[i]; - if (gg->group_family != gf) + struct genl_group *gg = genl_get_group(i); + if (gg == NULL || gg->group_family != gf) continue; int cmd_off = nlattr_add_nested(nw, ++cnt); @@ -376,6 +170,9 @@ /* Declare ourself as a user */ +static void nlctrl_notify(void *arg, const struct genl_family *gf, int action); +static eventhandler_tag family_event_tag; + #define CTRL_FAMILY_NAME "nlctrl" static uint32_t ctrl_family_id; @@ -430,8 +227,8 @@ if (attrs.family_id != 0 || attrs.family_name != NULL) { /* Resolve request */ for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = &families[i]; - if (match_family(gf, &attrs)) { + struct genl_family *gf = genl_get_family(i); + if (gf != NULL && match_family(gf, &attrs)) { error = dump_family(hdr, &ghdr, gf, npt->nw); return (error); } @@ -441,8 +238,8 @@ hdr->nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI; for (int i = 0; i < MAX_FAMILIES; i++) { - struct genl_family *gf = &families[i]; - if (match_family(gf, &attrs)) { + struct genl_family *gf = genl_get_family(i); + if (gf != NULL && match_family(gf, &attrs)) { error = dump_family(hdr, &ghdr, gf, npt->nw); if (error != 0) break; @@ -458,7 +255,7 @@ } static void -nlctrl_notify(const struct genl_family *gf, int cmd) +nlctrl_notify(void *arg __unused, const struct genl_family *gf, int cmd) { struct nlmsghdr hdr = {.nlmsg_type = NETLINK_GENERIC }; struct genlmsghdr ghdr = { .cmd = cmd }; @@ -481,37 +278,26 @@ }, }; -static void -genl_nlctrl_init(void) -{ - ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX); - genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds)); - ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify"); -} - -static void -genl_nlctrl_destroy(void) -{ - genl_unregister_family(CTRL_FAMILY_NAME); -} - static const struct nlhdr_parser *all_parsers[] = { &genl_parser }; static void -genl_load(void *u __unused) +genl_load_all(void *u __unused) { - GENL_LOCK_INIT(); NL_VERIFY_PARSERS(all_parsers); + ctrl_family_id = genl_register_family(CTRL_FAMILY_NAME, 0, 2, CTRL_ATTR_MAX); + genl_register_cmds(CTRL_FAMILY_NAME, nlctrl_cmds, NL_ARRAY_LEN(nlctrl_cmds)); + ctrl_group_id = genl_register_group(CTRL_FAMILY_NAME, "notify"); + family_event_tag = EVENTHANDLER_REGISTER(genl_family_event, nlctrl_notify, NULL, + EVENTHANDLER_PRI_ANY); netlink_register_proto(NETLINK_GENERIC, "NETLINK_GENERIC", genl_handle_message); - genl_nlctrl_init(); } -SYSINIT(genl_load, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load, NULL); +SYSINIT(genl_load_all, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_load_all, NULL); static void genl_unload(void *u __unused) { - genl_nlctrl_destroy(); - GENL_LOCK_DESTROY(); + EVENTHANDLER_DEREGISTER(genl_family_event, family_event_tag); + genl_unregister_family(CTRL_FAMILY_NAME); NET_EPOCH_WAIT(); } SYSUNINIT(genl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, genl_unload, NULL); diff --git a/sys/netlink/netlink_generic_kpi.c b/sys/netlink/netlink_generic_kpi.c new file mode 100644 --- /dev/null +++ b/sys/netlink/netlink_generic_kpi.c @@ -0,0 +1,260 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEBUG_MOD_NAME nl_generic_kpi +#define DEBUG_MAX_LEVEL LOG_DEBUG3 +#include +_DECLARE_DEBUG(LOG_DEBUG3); + + +/* + * NETLINK_GENERIC families/groups registration logic + */ + +#define GENL_LOCK() sx_xlock(&sx_lock) +#define GENL_UNLOCK() sx_xunlock(&sx_lock) +static struct sx sx_lock; +SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock"); + +static struct genl_family families[MAX_FAMILIES]; +static struct genl_group groups[MAX_GROUPS]; + +static struct genl_family * +find_family(const char *family_name) +{ + for (int i = 0; i < MAX_FAMILIES; i++) { + struct genl_family *gf = &families[i]; + if (gf->family_name != NULL && !strcmp(gf->family_name, family_name)) + return (gf); + } + + return (NULL); +} + +uint32_t +genl_register_family(const char *family_name, size_t hdrsize, int family_version, + int max_attr_idx) +{ + uint32_t family_id = 0; + + MPASS(family_name != NULL); + if (find_family(family_name) != NULL) + return (0); + + GENL_LOCK(); + for (int i = 0; i < MAX_FAMILIES; i++) { + struct genl_family *gf = &families[i]; + if (gf->family_name == NULL) { + gf->family_name = family_name; + gf->family_version = family_version; + gf->family_hdrsize = hdrsize; + gf->family_attr_max = max_attr_idx; + gf->family_id = i + GENL_MIN_ID; + NL_LOG(LOG_DEBUG2, "Registered family %s id %d", + gf->family_name, gf->family_id); + family_id = gf->family_id; + EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_NEWFAMILY); + break; + } + } + GENL_UNLOCK(); + + return (family_id); +} + +static void +free_family(struct genl_family *gf) +{ + if (gf->family_cmds != NULL) + free(gf->family_cmds, M_NETLINK); +} + +/* + * unregister groups of a given family + */ +static void +unregister_groups(const struct genl_family *gf) +{ + + for (int i = 0; i < MAX_GROUPS; i++) { + struct genl_group *gg = &groups[i]; + if (gg->group_family == gf && gg->group_name != NULL) { + gg->group_family = NULL; + gg->group_name = NULL; + } + } +} + +/* + * Can sleep, I guess + */ +bool +genl_unregister_family(const char *family_name) +{ + bool found = false; + + GENL_LOCK(); + struct genl_family *gf = find_family(family_name); + + if (gf != NULL) { + EVENTHANDLER_INVOKE(genl_family_event, gf, CTRL_CMD_DELFAMILY); + found = true; + unregister_groups(gf); + /* TODO: zero pointer first */ + free_family(gf); + bzero(gf, sizeof(*gf)); + } + GENL_UNLOCK(); + + return (found); +} + +bool +genl_register_cmds(const char *family_name, const struct genl_cmd *cmds, int count) +{ + GENL_LOCK(); + struct genl_family *gf = find_family(family_name); + if (gf == NULL) { + GENL_UNLOCK(); + return (false); + } + + int cmd_size = gf->family_cmd_size; + + for (int i = 0; i < count; i++) { + MPASS(cmds[i].cmd_cb != NULL); + if (cmds[i].cmd_num >= cmd_size) + cmd_size = cmds[i].cmd_num + 1; + } + + if (cmd_size > gf->family_cmd_size) { + /* need to realloc */ + size_t sz = cmd_size * sizeof(struct genl_cmd); + void *data = malloc(sz, M_NETLINK, M_WAITOK | M_ZERO); + + memcpy(data, gf->family_cmds, gf->family_cmd_size * sizeof(struct genl_cmd)); + void *old_data = gf->family_cmds; + gf->family_cmds = data; + gf->family_cmd_size = cmd_size; + free(old_data, M_NETLINK); + } + + for (int i = 0; i < count; i++) { + const struct genl_cmd *cmd = &cmds[i]; + MPASS(gf->family_cmds[cmd->cmd_num].cmd_cb == NULL); + gf->family_cmds[cmd->cmd_num] = cmds[i]; + NL_LOG(LOG_DEBUG2, "Adding cmd %s(%d) to family %s", + cmd->cmd_name, cmd->cmd_num, gf->family_name); + } + GENL_UNLOCK(); + return (true); +} + +static struct genl_group * +find_group(const struct genl_family *gf, const char *group_name) +{ + for (int i = 0; i < MAX_GROUPS; i++) { + struct genl_group *gg = &groups[i]; + if (gg->group_family == gf && !strcmp(gg->group_name, group_name)) + return (gg); + } + return (NULL); +} + +uint32_t +genl_register_group(const char *family_name, const char *group_name) +{ + uint32_t group_id = 0; + + MPASS(family_name != NULL); + MPASS(group_name != NULL); + + GENL_LOCK(); + struct genl_family *gf = find_family(family_name); + + if (gf == NULL || find_group(gf, group_name) != NULL) { + GENL_UNLOCK(); + return (0); + } + + for (int i = 0; i < MAX_GROUPS; i++) { + struct genl_group *gg = &groups[i]; + if (gg->group_family == NULL) { + gf->family_num_groups++; + gg->group_family = gf; + gg->group_name = group_name; + group_id = i + MIN_GROUP_NUM; + break; + } + } + GENL_UNLOCK(); + + return (group_id); +} + +/* accessors */ +struct genl_family * +genl_get_family(uint32_t family_id) +{ + return ((family_id < MAX_FAMILIES) ? &families[family_id] : NULL); +} + +const char * +genl_get_family_name(const struct genl_family *gf) +{ + return (gf->family_name); +} + +uint32_t +genl_get_family_id(const struct genl_family *gf) +{ + return (gf->family_id); +} + +struct genl_group * +genl_get_group(uint32_t group_id) +{ + return ((group_id < MAX_GROUPS) ? &groups[group_id] : NULL); +} + diff --git a/sys/netlink/netlink_glue.c b/sys/netlink/netlink_glue.c new file mode 100644 --- /dev/null +++ b/sys/netlink/netlink_glue.c @@ -0,0 +1,260 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2023 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_netlink.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* priv_check */ + +#include +#include + +#include +#include +#include + +/* Standard bits: built-in the kernel */ +SYSCTL_NODE(_net, OID_AUTO, netlink, CTLFLAG_RD, 0, ""); +SYSCTL_NODE(_net_netlink, OID_AUTO, debug, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); + +MALLOC_DEFINE(M_NETLINK, "netlink", "Memory used for netlink packets"); + +/* Netlink-related callbacks needed to glue rtsock, netlink and linuxolator */ +static void +ignore_route_event(uint32_t fibnum, const struct rib_cmd_info *rc) +{ +} + +static void +ignore_ifmsg_event(struct ifnet *ifp, int if_flags_mask) +{ +} + +static struct rtbridge ignore_cb = { + .route_f = ignore_route_event, + .ifmsg_f = ignore_ifmsg_event, +}; + +void *linux_netlink_p = NULL; /* Callback pointer for Linux translator functions */ +struct rtbridge *rtsock_callback_p = &ignore_cb; +struct rtbridge *netlink_callback_p = &ignore_cb; + + +/* + * nlp accessors. + * TODO: move to a separate file once the number grows. + */ +bool +nlp_has_priv(struct nlpcb *nlp, int priv) +{ + return (priv_check_cred(nlp->nl_cred, priv) == 0); +} + +struct ucred * +nlp_get_cred(struct nlpcb *nlp) +{ + return (nlp->nl_cred); +} + +uint32_t +nlp_get_pid(const struct nlpcb *nlp) +{ + return (nlp->nl_process_id); +} + +#ifndef NETLINK +/* Stub implementations for the loadable functions */ + +static bool +get_stub_writer(struct nl_writer *nw) +{ + bzero(nw, sizeof(*nw)); + nw->writer_type = NS_WRITER_TYPE_STUB; + nw->enomem = true; + + return (false); +} + +static bool +nlmsg_get_unicast_writer_stub(struct nl_writer *nw, int size, struct nlpcb *nlp) +{ + return (get_stub_writer(nw)); +} + +static bool +nlmsg_get_group_writer_stub(struct nl_writer *nw, int size, int protocol, int group_id) +{ + return (get_stub_writer(nw)); +} + +static bool +nlmsg_get_chain_writer_stub(struct nl_writer *nw, int size, struct mbuf **pm) +{ + return (get_stub_writer(nw)); +} + +static bool +nlmsg_flush_stub(struct nl_writer *nw __unused) +{ + return (false); +} + +static void +nlmsg_ignore_limit_stub(struct nl_writer *nw __unused) +{ +} + +static bool +nlmsg_refill_buffer_stub(struct nl_writer *nw __unused, int required_len __unused) +{ + return (false); +} + +static bool +nlmsg_add_stub(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, + uint16_t flags, uint32_t len) +{ + return (false); +} + +static bool +nlmsg_end_stub(struct nl_writer *nw __unused) +{ + return (false); +} + +static void +nlmsg_abort_stub(struct nl_writer *nw __unused) +{ +} + +static bool +nlmsg_end_dump_stub(struct nl_writer *nw, int error, struct nlmsghdr *hdr) +{ + return (false); +} + +const static struct nl_function_wrapper nl_stub = { + .nlmsg_add = nlmsg_add_stub, + .nlmsg_refill_buffer = nlmsg_refill_buffer_stub, + .nlmsg_flush = nlmsg_flush_stub, + .nlmsg_end = nlmsg_end_stub, + .nlmsg_abort = nlmsg_abort_stub, + .nlmsg_ignore_limit = nlmsg_ignore_limit_stub, + .nlmsg_get_unicast_writer = nlmsg_get_unicast_writer_stub, + .nlmsg_get_group_writer = nlmsg_get_group_writer_stub, + .nlmsg_get_chain_writer = nlmsg_get_chain_writer_stub, + .nlmsg_end_dump = nlmsg_end_dump_stub, +}; + +/* + * If the kernel is compiled with netlink as a module, + * provide a way to introduce non-stub functioms + */ +static const struct nl_function_wrapper *_nl = &nl_stub; + +void +nl_set_functions(const struct nl_function_wrapper *nl) +{ + _nl = (nl != NULL) ? nl : &nl_stub; +} + +/* Function wrappers */ +bool +nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp) +{ + return (_nl->nlmsg_get_unicast_writer(nw, size, nlp)); +} + +bool +nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id) +{ + return (_nl->nlmsg_get_group_writer(nw, size, protocol, group_id)); +} + +bool +nlmsg_get_chain_writer(struct nl_writer *nw, int size, struct mbuf **pm) +{ + return (_nl->nlmsg_get_chain_writer(nw, size, pm)); +} + +bool +nlmsg_flush(struct nl_writer *nw) +{ + return (_nl->nlmsg_flush(nw)); +} + +void nlmsg_ignore_limit(struct nl_writer *nw) +{ + _nl->nlmsg_ignore_limit(nw); +} + +bool +nlmsg_refill_buffer(struct nl_writer *nw, int required_len) +{ + return (_nl->nlmsg_refill_buffer(nw, required_len)); +} + +bool +nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, + uint16_t flags, uint32_t len) +{ + return (_nl->nlmsg_add(nw, portid, seq, type, flags, len)); +} + +bool +nlmsg_end(struct nl_writer *nw) +{ + return (_nl->nlmsg_end(nw)); +} + +void +nlmsg_abort(struct nl_writer *nw) +{ + _nl->nlmsg_abort(nw); +} + +bool +nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr) +{ + return (_nl->nlmsg_end_dump(nw, error, hdr)); +} +#endif /* !NETLINK */ + diff --git a/sys/netlink/netlink_message_parser.c b/sys/netlink/netlink_message_parser.c --- a/sys/netlink/netlink_message_parser.c +++ b/sys/netlink/netlink_message_parser.c @@ -46,6 +46,8 @@ #include #include +/* Parsers are build by default */ + #define DEBUG_MOD_NAME nl_parser #define DEBUG_MAX_LEVEL LOG_DEBUG3 #include diff --git a/sys/netlink/netlink_message_writer.h b/sys/netlink/netlink_message_writer.h --- a/sys/netlink/netlink_message_writer.h +++ b/sys/netlink/netlink_message_writer.h @@ -65,6 +65,7 @@ #define NS_WRITER_TYPE_BUF 1 #define NS_WRITER_TYPE_LBUF 2 #define NS_WRITER_TYPE_MBUFC 3 +#define NS_WRITER_TYPE_STUB 4 #define NLMSG_SMALL 128 @@ -73,6 +74,89 @@ /* Message and attribute writing */ struct nlpcb; + +#if defined(NETLINK) || defined(NETLINK_MODULE) +/* Provide optimized calls to the functions inside the same linking unit */ + +bool _nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp); +bool _nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id); +bool _nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm); +bool _nlmsg_flush(struct nl_writer *nw); +void _nlmsg_ignore_limit(struct nl_writer *nw); + +bool _nlmsg_refill_buffer(struct nl_writer *nw, int required_size); +bool _nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, + uint16_t flags, uint32_t len); +bool _nlmsg_end(struct nl_writer *nw); +void _nlmsg_abort(struct nl_writer *nw); + +bool _nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr); + + +static inline bool +nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp) +{ + return (_nlmsg_get_unicast_writer(nw, expected_size, nlp)); +} + +static inline bool +nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id) +{ + return (_nlmsg_get_group_writer(nw, expected_size, proto, group_id)); +} + +static inline bool +nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm) +{ + return (_nlmsg_get_chain_writer(nw, expected_size, pm)); +} + +static inline bool +nlmsg_flush(struct nl_writer *nw) +{ + return (_nlmsg_flush(nw)); +} + +static inline void +nlmsg_ignore_limit(struct nl_writer *nw) +{ + _nlmsg_ignore_limit(nw); +} + +static inline bool +nlmsg_refill_buffer(struct nl_writer *nw, int required_size) +{ + return (_nlmsg_refill_buffer(nw, required_size)); +} + +static inline bool +nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, + uint16_t flags, uint32_t len) +{ + return (_nlmsg_add(nw, portid, seq, type, flags, len)); +} + +static inline bool +nlmsg_end(struct nl_writer *nw) +{ + return (_nlmsg_end(nw)); +} + +static inline void +nlmsg_abort(struct nl_writer *nw) +{ + return (_nlmsg_abort(nw)); +} + +static inline bool +nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr) +{ + return (_nlmsg_end_dump(nw, error, hdr)); +} + +#else +/* Provide access to the functions via netlink_glue.c */ + bool nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp); bool nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id); bool nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm); @@ -87,6 +171,8 @@ bool nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr); +#endif /* defined(NETLINK) || defined(NETLINK_MODULE) */ + static inline bool nlmsg_reply(struct nl_writer *nw, const struct nlmsghdr *hdr, int payload_len) { diff --git a/sys/netlink/netlink_message_writer.c b/sys/netlink/netlink_message_writer.c --- a/sys/netlink/netlink_message_writer.c +++ b/sys/netlink/netlink_message_writer.c @@ -434,7 +434,7 @@ } bool -nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp) +_nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp) { if (!nlmsg_get_buf(nw, size, false, nlp->nl_linux)) return (false); @@ -445,7 +445,7 @@ } bool -nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id) +_nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id) { if (!nlmsg_get_buf(nw, size, false, false)) return (false); @@ -456,7 +456,7 @@ } bool -nlmsg_get_chain_writer(struct nl_writer *nw, int size, struct mbuf **pm) +_nlmsg_get_chain_writer(struct nl_writer *nw, int size, struct mbuf **pm) { if (!nlmsg_get_buf(nw, size, false, false)) return (false); @@ -469,13 +469,13 @@ } void -nlmsg_ignore_limit(struct nl_writer *nw) +_nlmsg_ignore_limit(struct nl_writer *nw) { nw->ignore_limit = true; } bool -nlmsg_flush(struct nl_writer *nw) +_nlmsg_flush(struct nl_writer *nw) { if (__predict_false(nw->hdr != NULL)) { @@ -503,7 +503,7 @@ * Return true on success. */ bool -nlmsg_refill_buffer(struct nl_writer *nw, int required_len) +_nlmsg_refill_buffer(struct nl_writer *nw, int required_len) { struct nl_writer ns_new = {}; int completed_len, new_len; @@ -561,7 +561,7 @@ } bool -nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, +_nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, uint16_t flags, uint32_t len) { struct nlmsghdr *hdr; @@ -589,7 +589,7 @@ } bool -nlmsg_end(struct nl_writer *nw) +_nlmsg_end(struct nl_writer *nw) { MPASS(nw->hdr != NULL); @@ -609,7 +609,7 @@ } void -nlmsg_abort(struct nl_writer *nw) +_nlmsg_abort(struct nl_writer *nw) { if (nw->hdr != NULL) { nw->offset = (uint32_t)((char *)nw->hdr - nw->data); @@ -670,7 +670,7 @@ } bool -nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr) +_nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr) { if (!nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, NLMSG_DONE, 0, sizeof(int))) { NL_LOG(LOG_DEBUG, "Error finalizing table dump"); diff --git a/sys/netlink/netlink_module.c b/sys/netlink/netlink_module.c --- a/sys/netlink/netlink_module.c +++ b/sys/netlink/netlink_module.c @@ -26,6 +26,8 @@ * SUCH DAMAGE. */ +#include "opt_netlink.h" + #include __FBSDID("$FreeBSD$"); #include @@ -44,7 +46,6 @@ #include -MALLOC_DEFINE(M_NETLINK, "netlink", "Memory used for netlink packets"); FEATURE(netlink, "Netlink support"); #define DEBUG_MOD_NAME nl_mod @@ -52,8 +53,6 @@ #include _DECLARE_DEBUG(LOG_DEBUG); -SYSCTL_NODE(_net, OID_AUTO, netlink, CTLFLAG_RD, 0, ""); -SYSCTL_NODE(_net_netlink, OID_AUTO, debug, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); #define NL_MAX_HANDLERS 20 struct nl_proto_handler _nl_handlers[NL_MAX_HANDLERS]; @@ -175,6 +174,21 @@ return (true); } +#ifdef NETLINK_MODULE +/* Non-stub function provider */ +const static struct nl_function_wrapper nl_module = { + .nlmsg_add = _nlmsg_add, + .nlmsg_refill_buffer = _nlmsg_refill_buffer, + .nlmsg_flush = _nlmsg_flush, + .nlmsg_end = _nlmsg_end, + .nlmsg_abort = _nlmsg_abort, + .nlmsg_get_unicast_writer = _nlmsg_get_unicast_writer, + .nlmsg_get_group_writer = _nlmsg_get_group_writer, + .nlmsg_get_chain_writer = _nlmsg_get_chain_writer, + .nlmsg_end_dump = _nlmsg_end_dump, +}; +#endif + static bool can_unload(void) { @@ -205,6 +219,9 @@ switch (what) { case MOD_LOAD: NL_LOG(LOG_DEBUG2, "Loading"); +#if !defined(NETLINK) && defined(NETLINK_MODULE) + nl_set_functions(&nl_module); +#endif break; case MOD_UNLOAD: @@ -212,6 +229,9 @@ if (can_unload()) { NL_LOG(LOG_WARNING, "unloading"); netlink_unloading = 1; +#if !defined(NETLINK) && defined(NETLINK_MODULE) + nl_set_functions(NULL); +#endif } else ret = EBUSY; break; diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -89,6 +89,7 @@ #define NLF_STRICT 0x04 /* Perform strict header checks */ SYSCTL_DECL(_net_netlink); +SYSCTL_DECL(_net_netlink_debug); struct nl_io { struct callout callout; @@ -143,5 +144,47 @@ int nl_receive_async(struct mbuf *m, struct socket *so); void nl_process_receive_locked(struct nlpcb *nlp); +/* netlink_generic.c */ +struct genl_family { + const char *family_name; + uint16_t family_hdrsize; + uint16_t family_id; + uint16_t family_version; + uint16_t family_attr_max; + uint16_t family_cmd_size; + uint16_t family_num_groups; + struct genl_cmd *family_cmds; +}; + +struct genl_group { + struct genl_family *group_family; + const char *group_name; +}; + +struct genl_family *genl_get_family(uint32_t family_id); +struct genl_group *genl_get_group(uint32_t group_id); + +#define MAX_FAMILIES 20 +#define MAX_GROUPS 64 + +#define MIN_GROUP_NUM 48 + + +/* Function map */ +struct nl_function_wrapper { + bool (*nlmsg_add)(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, + uint16_t flags, uint32_t len); + bool (*nlmsg_refill_buffer)(struct nl_writer *nw, int required_len); + bool (*nlmsg_flush)(struct nl_writer *nw); + bool (*nlmsg_end)(struct nl_writer *nw); + void (*nlmsg_abort)(struct nl_writer *nw); + void (*nlmsg_ignore_limit)(struct nl_writer *nw); + bool (*nlmsg_get_unicast_writer)(struct nl_writer *nw, int size, struct nlpcb *nlp); + bool (*nlmsg_get_group_writer)(struct nl_writer *nw, int size, int protocol, int group_id); + bool (*nlmsg_get_chain_writer)(struct nl_writer *nw, int size, struct mbuf **pm); + bool (*nlmsg_end_dump)(struct nl_writer *nw, int error, struct nlmsghdr *hdr); +}; +void nl_set_functions(const struct nl_function_wrapper *nl); + #endif #endif