/* $NetBSD: rtsock.c,v 1.256 2022/08/27 08:36:41 skrll Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * 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. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. */ /* * Copyright (c) 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)rtsock.c 8.7 (Berkeley) 10/12/95 */ #include __KERNEL_RCSID(0, "$NetBSD: rtsock.c,v 1.256 2022/08/27 08:36:41 skrll Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" #include "opt_compat_netbsd.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_RTSOCK #undef COMPAT_RTSOCK #endif static int if_addrflags(struct ifaddr *); #include /* * XXX avoid using void * once msghdr compat disappears. */ void rt_setmetrics(void *in, struct rtentry *out) { const struct rt_xmsghdr *rtm = in; _rt_setmetrics(rtm->rtm_inits, rtm, out); } int rt_msg3(int type, struct rt_addrinfo *rtinfo, void *cpv, struct rt_walkarg *w, int *lenp) { return rt_msg2(type, rtinfo, cpv, w, lenp); } static int if_addrflags(struct ifaddr *ifa) { switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: return ifatoia(ifa)->ia4_flags; #endif #ifdef INET6 case AF_INET6: return ifatoia6(ifa)->ia6_flags; #endif default: return 0; } } /* * Send a routing message as mimicing that a cloned route is added. */ void rt_clonedmsg(int type, const struct sockaddr *src, const struct sockaddr *dst, const uint8_t *lladdr, const struct ifnet *ifp) { struct rt_addrinfo info; /* Mimic flags exactly */ #define RTF_LLINFO 0x400 #define RTF_CLONED 0x2000 int flags = RTF_DONE; union { struct sockaddr sa; struct sockaddr_storage ss; struct sockaddr_dl sdl; } u; if (type != RTM_MISS) flags |= RTF_HOST | RTF_CLONED | RTF_LLINFO; if (type == RTM_ADD || type == RTM_CHANGE) flags |= RTF_UP; memset(&info, 0, sizeof(info)); info.rti_info[RTAX_AUTHOR] = src; info.rti_info[RTAX_DST] = dst; sockaddr_dl_init(&u.sdl, sizeof(u.ss), ifp->if_index, ifp->if_type, NULL, 0, lladdr, ifp->if_addrlen); info.rti_info[RTAX_GATEWAY] = &u.sa; rt_missmsg(type, &info, flags, 0); #undef RTF_LLINFO #undef RTF_CLONED } /* * The remaining code implements the routing-table sysctl node. It is * compiled only for the non-COMPAT case. */ /* * This is used in dumping the kernel table via sysctl(). */ static int sysctl_dumpentry(struct rtentry *rt, void *v) { struct rt_walkarg *w = v; int error = 0, size; struct rt_addrinfo info; if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) return 0; memset(&info, 0, sizeof(info)); info.rti_info[RTAX_DST] = rt_getkey(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); info.rti_info[RTAX_TAG] = rt_gettag(rt); if (rt->rt_ifp) { const struct ifaddr *rtifa; info.rti_info[RTAX_IFP] = rt->rt_ifp->if_dl->ifa_addr; /* rtifa used to be simply rt->rt_ifa. If rt->rt_ifa != NULL, * then rt_get_ifa() != NULL. So this ought to still be safe. * --dyoung */ rtifa = rt_get_ifa(rt); info.rti_info[RTAX_IFA] = rtifa->ifa_addr; if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) info.rti_info[RTAX_BRD] = rtifa->ifa_dstaddr; } if ((error = rt_msg2(RTM_GET, &info, 0, w, &size))) return error; if (w->w_where && w->w_tmem && w->w_needed <= 0) { struct rt_xmsghdr *rtm = (struct rt_xmsghdr *)w->w_tmem; rtm->rtm_flags = rt->rt_flags; rtm->rtm_use = rt->rt_use; rtm_setmetrics(rt, rtm); KASSERT(rt->rt_ifp != NULL); rtm->rtm_index = rt->rt_ifp->if_index; rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0; rtm->rtm_addrs = info.rti_addrs; if ((error = copyout(rtm, w->w_where, size)) != 0) w->w_where = NULL; else w->w_where = (char *)w->w_where + size; } return error; } static int sysctl_iflist_if(struct ifnet *ifp, struct rt_walkarg *w, struct rt_addrinfo *info, size_t len) { struct if_xmsghdr *ifm; int error; ifm = (struct if_xmsghdr *)w->w_tmem; ifm->ifm_index = ifp->if_index; ifm->ifm_flags = ifp->if_flags; if_export_if_data(ifp, &ifm->ifm_data, false); ifm->ifm_addrs = info->rti_addrs; if ((error = copyout(ifm, w->w_where, len)) == 0) w->w_where = (char *)w->w_where + len; return error; } static int sysctl_iflist_addr(struct rt_walkarg *w, struct ifaddr *ifa, struct rt_addrinfo *info) { int len, error; if ((error = rt_msg2(RTM_XNEWADDR, info, 0, w, &len))) return error; if (w->w_where && w->w_tmem && w->w_needed <= 0) { struct ifa_xmsghdr *ifam; ifam = (struct ifa_xmsghdr *)w->w_tmem; ifam->ifam_index = ifa->ifa_ifp->if_index; ifam->ifam_flags = ifa->ifa_flags; ifam->ifam_metric = ifa->ifa_metric; ifam->ifam_addrs = info->rti_addrs; ifam->ifam_pid = 0; ifam->ifam_addrflags = if_addrflags(ifa); if ((error = copyout(w->w_tmem, w->w_where, len)) == 0) w->w_where = (char *)w->w_where + len; } return error; } static int sysctl_iflist(int af, struct rt_walkarg *w, int type) { struct ifnet *ifp; struct ifaddr *ifa; struct rt_addrinfo info; int cmd, len, error = 0; int s; struct psref psref; int bound; switch (type) { case NET_RT_IFLIST: cmd = RTM_IFINFO; break; case NET_RT_OOOIFLIST: cmd = RTM_OOIFINFO; break; case NET_RT_OOIFLIST: cmd = RTM_OIFINFO; break; case NET_RT_OIFLIST: cmd = RTM_IFINFO; break; default: #ifdef RTSOCK_DEBUG printf("%s: unsupported IFLIST type %d\n", __func__, type); #endif return EINVAL; } memset(&info, 0, sizeof(info)); bound = curlwp_bind(); s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { int _s; if (w->w_arg && w->w_arg != ifp->if_index) continue; if (IFADDR_READER_EMPTY(ifp)) continue; if_acquire(ifp, &psref); pserialize_read_exit(s); info.rti_info[RTAX_IFP] = ifp->if_dl->ifa_addr; if ((error = rt_msg2(cmd, &info, NULL, w, &len)) != 0) goto release_exit; info.rti_info[RTAX_IFP] = NULL; if (w->w_where && w->w_tmem && w->w_needed <= 0) { switch (type) { case NET_RT_OIFLIST: /* old _70 */ if (!rtsock_iflist_70_hook.hooked) { error = EINVAL; break; } /* FALLTHROUGH */ case NET_RT_IFLIST: /* current */ error = sysctl_iflist_if(ifp, w, &info, len); break; case NET_RT_OOIFLIST: /* old _50 */ MODULE_HOOK_CALL(rtsock_iflist_50_hook, (ifp, w, &info, len), enosys(), error); break; case NET_RT_OOOIFLIST: /* old _14 */ MODULE_HOOK_CALL(rtsock_iflist_14_hook, (ifp, w, &info, len), enosys(), error); break; default: error = EINVAL; } if (error != 0) { if (error == ENOSYS) error = EINVAL; goto release_exit; } } _s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { struct psref _psref; if (af && af != ifa->ifa_addr->sa_family) continue; ifa_acquire(ifa, &_psref); pserialize_read_exit(_s); info.rti_info[RTAX_IFA] = ifa->ifa_addr; info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask; info.rti_info[RTAX_BRD] = ifa->ifa_dstaddr; switch (type) { case NET_RT_IFLIST: error = sysctl_iflist_addr(w, ifa, &info); break; case NET_RT_OIFLIST: case NET_RT_OOIFLIST: case NET_RT_OOOIFLIST: MODULE_HOOK_CALL(rtsock_iflist_70_hook, (w, ifa, &info), enosys(), error); break; default: error = EINVAL; } _s = pserialize_read_enter(); ifa_release(ifa, &_psref); if (error != 0) { pserialize_read_exit(_s); goto release_exit; } } pserialize_read_exit(_s); info.rti_info[RTAX_IFA] = info.rti_info[RTAX_NETMASK] = info.rti_info[RTAX_BRD] = NULL; s = pserialize_read_enter(); if_release(ifp, &psref); } pserialize_read_exit(s); curlwp_bindx(bound); return 0; release_exit: if_release(ifp, &psref); curlwp_bindx(bound); return error; } static int sysctl_rtable(SYSCTLFN_ARGS) { void *where = oldp; size_t *given = oldlenp; int i, error = EINVAL; u_char af; struct rt_walkarg w; if (namelen == 1 && name[0] == CTL_QUERY) return sysctl_query(SYSCTLFN_CALL(rnode)); if (newp) return EPERM; if (namelen != 3) return EINVAL; af = name[0]; w.w_tmemneeded = 0; w.w_tmemsize = 0; w.w_tmem = NULL; again: /* we may return here if a later [re]alloc of the t_mem buffer fails */ if (w.w_tmemneeded) { w.w_tmem = kmem_zalloc(w.w_tmemneeded, KM_SLEEP); w.w_tmemsize = w.w_tmemneeded; w.w_tmemneeded = 0; } w.w_op = name[1]; w.w_arg = name[2]; w.w_given = *given; w.w_needed = 0 - w.w_given; w.w_where = where; KERNEL_LOCK_UNLESS_NET_MPSAFE(); const int s = splsoftnet(); switch (w.w_op) { case NET_RT_DUMP: case NET_RT_FLAGS: #if defined(INET) || defined(INET6) /* * take care of llinfo entries, the caller must * specify an AF */ if (w.w_op == NET_RT_FLAGS && (w.w_arg == 0 || w.w_arg & RTF_LLDATA)) { if (af != 0) error = lltable_sysctl_dump(af, &w); else error = EINVAL; break; } #endif for (i = 1; i <= AF_MAX; i++) { if (af == 0 || af == i) { error = rt_walktree(i, sysctl_dumpentry, &w); if (error != 0) break; #if defined(INET) || defined(INET6) /* * Return ARP/NDP entries too for * backward compatibility. */ error = lltable_sysctl_dump(i, &w); if (error != 0) break; #endif } } break; case NET_RT_OOOIFLIST: /* compat_14 */ case NET_RT_OOIFLIST: /* compat_50 */ case NET_RT_OIFLIST: /* compat_70 */ case NET_RT_IFLIST: /* current */ error = sysctl_iflist(af, &w, w.w_op); break; } splx(s); KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); /* check to see if we couldn't allocate memory with NOWAIT */ if (error == ENOBUFS && w.w_tmem == 0 && w.w_tmemneeded) goto again; if (w.w_tmem) kmem_free(w.w_tmem, w.w_tmemsize); w.w_needed += w.w_given; if (where) { *given = (char *)w.w_where - (char *)where; if (*given < w.w_needed) return ENOMEM; } else { *given = (11 * w.w_needed) / 10; } return error; } void sysctl_net_route_setup(struct sysctllog **clog, int pf, const char *name) { const struct sysctlnode *rnode = NULL; sysctl_createv(clog, 0, NULL, &rnode, CTLFLAG_PERMANENT, CTLTYPE_NODE, name, SYSCTL_DESCR("PF_ROUTE information"), NULL, 0, NULL, 0, CTL_NET, pf, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT, CTLTYPE_NODE, "rtable", SYSCTL_DESCR("Routing table information"), sysctl_rtable, 0, NULL, 0, CTL_NET, pf, 0 /* any protocol */, CTL_EOL); sysctl_createv(clog, 0, &rnode, NULL, CTLFLAG_PERMANENT, CTLTYPE_STRUCT, "stats", SYSCTL_DESCR("Routing statistics"), NULL, 0, &rtstat, sizeof(rtstat), CTL_CREATE, CTL_EOL); }