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> 31fc083c3eSJung-uk Kim #include <sys/ck.h> 32669d63ebSAlexander V. Chernikov #include <sys/epoch.h> 33fc083c3eSJung-uk Kim #include <sys/kernel.h> 347e5bf684SAlexander V. Chernikov #include <sys/malloc.h> 357e5bf684SAlexander V. Chernikov #include <sys/socket.h> 367e5bf684SAlexander V. Chernikov 377e5bf684SAlexander V. Chernikov #include <net/route.h> 387e5bf684SAlexander V. Chernikov #include <net/route/route_ctl.h> 397e5bf684SAlexander V. Chernikov #include <netlink/netlink.h> 407e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 417e5bf684SAlexander V. Chernikov #include <netlink/netlink_route.h> 427e5bf684SAlexander V. Chernikov #include <netlink/route/route_var.h> 437e5bf684SAlexander V. Chernikov 447e5bf684SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_route_core 457e5bf684SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3 467e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h> 477e5bf684SAlexander V. Chernikov _DECLARE_DEBUG(LOG_DEBUG); 487e5bf684SAlexander V. Chernikov 497e5bf684SAlexander V. Chernikov #define HANDLER_MAX_NUM (NL_RTM_MAX + 10) 507e5bf684SAlexander V. Chernikov static const struct rtnl_cmd_handler *rtnl_handler[HANDLER_MAX_NUM] = {}; 517e5bf684SAlexander V. Chernikov 527e5bf684SAlexander V. Chernikov bool 537e5bf684SAlexander V. Chernikov rtnl_register_messages(const struct rtnl_cmd_handler *handlers, int count) 547e5bf684SAlexander V. Chernikov { 557e5bf684SAlexander V. Chernikov for (int i = 0; i < count; i++) { 567e5bf684SAlexander V. Chernikov if (handlers[i].cmd >= HANDLER_MAX_NUM) 577e5bf684SAlexander V. Chernikov return (false); 587e5bf684SAlexander V. Chernikov MPASS(rtnl_handler[handlers[i].cmd] == NULL); 597e5bf684SAlexander V. Chernikov } 607e5bf684SAlexander V. Chernikov for (int i = 0; i < count; i++) 617e5bf684SAlexander V. Chernikov rtnl_handler[handlers[i].cmd] = &handlers[i]; 627e5bf684SAlexander V. Chernikov return (true); 637e5bf684SAlexander V. Chernikov } 647e5bf684SAlexander V. Chernikov 657e5bf684SAlexander V. Chernikov /* 667e5bf684SAlexander V. Chernikov * Handler called by netlink subsystem when matching netlink message is received 677e5bf684SAlexander V. Chernikov */ 687e5bf684SAlexander V. Chernikov static int 697e5bf684SAlexander V. Chernikov rtnl_handle_message(struct nlmsghdr *hdr, struct nl_pstate *npt) 707e5bf684SAlexander V. Chernikov { 717e5bf684SAlexander V. Chernikov const struct rtnl_cmd_handler *cmd; 727e5bf684SAlexander V. Chernikov struct epoch_tracker et; 737e5bf684SAlexander V. Chernikov struct nlpcb *nlp = npt->nlp; 747e5bf684SAlexander V. Chernikov int error = 0; 757e5bf684SAlexander V. Chernikov 767e5bf684SAlexander V. Chernikov if (__predict_false(hdr->nlmsg_type >= HANDLER_MAX_NUM)) { 777e5bf684SAlexander V. Chernikov NLMSG_REPORT_ERR_MSG(npt, "unknown message type: %d", hdr->nlmsg_type); 787e5bf684SAlexander V. Chernikov return (ENOTSUP); 797e5bf684SAlexander V. Chernikov } 807e5bf684SAlexander V. Chernikov 817e5bf684SAlexander V. Chernikov cmd = rtnl_handler[hdr->nlmsg_type]; 827e5bf684SAlexander V. Chernikov if (__predict_false(cmd == NULL)) { 837e5bf684SAlexander V. Chernikov NLMSG_REPORT_ERR_MSG(npt, "unknown message type: %d", hdr->nlmsg_type); 847e5bf684SAlexander V. Chernikov return (ENOTSUP); 857e5bf684SAlexander V. Chernikov } 867e5bf684SAlexander V. Chernikov 877e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "received msg %s(%d) len %d", cmd->name, 887e5bf684SAlexander V. Chernikov hdr->nlmsg_type, hdr->nlmsg_len); 897e5bf684SAlexander V. Chernikov 907e5bf684SAlexander V. Chernikov if (cmd->priv != 0 && !nlp_has_priv(nlp, cmd->priv)) { 917e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "priv %d check failed for msg %s", cmd->priv, cmd->name); 927e5bf684SAlexander V. Chernikov return (EPERM); 937e5bf684SAlexander V. Chernikov } else if (cmd->priv != 0) 947e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG3, nlp, "priv %d check passed for msg %s", cmd->priv, cmd->name); 957e5bf684SAlexander V. Chernikov 96*04f75b98SAlexander V. Chernikov if (!nlp_unconstrained_vnet(nlp) && (cmd->flags & RTNL_F_ALLOW_NONVNET_JAIL) == 0) { 97*04f75b98SAlexander V. Chernikov NLP_LOG(LOG_DEBUG2, nlp, "jail check failed for msg %s", cmd->name); 98*04f75b98SAlexander V. Chernikov return (EPERM); 99*04f75b98SAlexander V. Chernikov } 100*04f75b98SAlexander V. Chernikov 1017e5bf684SAlexander V. Chernikov bool need_epoch = !(cmd->flags & RTNL_F_NOEPOCH); 1027e5bf684SAlexander V. Chernikov 1037e5bf684SAlexander V. Chernikov if (need_epoch) 1047e5bf684SAlexander V. Chernikov NET_EPOCH_ENTER(et); 1057e5bf684SAlexander V. Chernikov error = cmd->cb(hdr, nlp, npt); 1067e5bf684SAlexander V. Chernikov if (need_epoch) 1077e5bf684SAlexander V. Chernikov NET_EPOCH_EXIT(et); 1087e5bf684SAlexander V. Chernikov 1097e5bf684SAlexander V. Chernikov NLP_LOG(LOG_DEBUG3, nlp, "message %s -> error %d", cmd->name, error); 1107e5bf684SAlexander V. Chernikov 1117e5bf684SAlexander V. Chernikov return (error); 1127e5bf684SAlexander V. Chernikov } 1137e5bf684SAlexander V. Chernikov 1141bcd230fSAlexander V. Chernikov static struct rtbridge nlbridge = { 1151bcd230fSAlexander V. Chernikov .route_f = rtnl_handle_route_event, 1161bcd230fSAlexander V. Chernikov .ifmsg_f = rtnl_handle_ifnet_event, 1171bcd230fSAlexander V. Chernikov }; 1187e5bf684SAlexander V. Chernikov static struct rtbridge *nlbridge_orig_p; 1197e5bf684SAlexander V. Chernikov 1207e5bf684SAlexander V. Chernikov static void 1217e5bf684SAlexander V. Chernikov rtnl_load(void *u __unused) 1227e5bf684SAlexander V. Chernikov { 1237fc9cfa5SAlexander V. Chernikov NL_LOG(LOG_DEBUG2, "rtnl loading"); 1247e5bf684SAlexander V. Chernikov nlbridge_orig_p = netlink_callback_p; 1257e5bf684SAlexander V. Chernikov netlink_callback_p = &nlbridge; 1267e5bf684SAlexander V. Chernikov rtnl_neighs_init(); 1277e5bf684SAlexander V. Chernikov rtnl_ifaces_init(); 1287e5bf684SAlexander V. Chernikov rtnl_nexthops_init(); 1297e5bf684SAlexander V. Chernikov rtnl_routes_init(); 1307e5bf684SAlexander V. Chernikov netlink_register_proto(NETLINK_ROUTE, "NETLINK_ROUTE", rtnl_handle_message); 1317e5bf684SAlexander V. Chernikov } 1327e5bf684SAlexander V. Chernikov SYSINIT(rtnl_load, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, rtnl_load, NULL); 1337e5bf684SAlexander V. Chernikov 1347e5bf684SAlexander V. Chernikov static void 1357e5bf684SAlexander V. Chernikov rtnl_unload(void *u __unused) 1367e5bf684SAlexander V. Chernikov { 1377e5bf684SAlexander V. Chernikov netlink_callback_p = nlbridge_orig_p; 1387e5bf684SAlexander V. Chernikov rtnl_ifaces_destroy(); 1397e5bf684SAlexander V. Chernikov rtnl_neighs_destroy(); 1407e5bf684SAlexander V. Chernikov 1417e5bf684SAlexander V. Chernikov /* Wait till all consumers read nlbridge data */ 142ab591c87SZhenlei Huang NET_EPOCH_WAIT(); 1437e5bf684SAlexander V. Chernikov } 1447e5bf684SAlexander V. Chernikov SYSUNINIT(rtnl_unload, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, rtnl_unload, NULL); 145