1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2022 Alexander V. Chernikov 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 "opt_inet.h" 31 #include "opt_inet6.h" 32 #include "opt_route.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/malloc.h> 37 #include <sys/socket.h> 38 #include <sys/jail.h> 39 #include <sys/kernel.h> 40 #include <sys/lock.h> 41 #include <sys/rmlock.h> 42 43 #include <net/if.h> 44 #include <net/if_var.h> 45 #include <net/vnet.h> 46 #include <net/route.h> 47 #include <net/route/route_ctl.h> 48 #include <net/route/route_var.h> 49 #include <net/route/nhop.h> 50 #include <netinet/in.h> 51 #include <netinet6/scope6_var.h> 52 53 #include <vm/uma.h> 54 55 /* Routing table UMA zone */ 56 VNET_DEFINE_STATIC(uma_zone_t, rtzone); 57 #define V_rtzone VNET(rtzone) 58 59 void 60 vnet_rtzone_init(void) 61 { 62 63 V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), 64 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 65 } 66 67 #ifdef VIMAGE 68 void 69 vnet_rtzone_destroy(void) 70 { 71 72 uma_zdestroy(V_rtzone); 73 } 74 #endif 75 76 /* 77 * Creates rtentry and based on @dst/@netmask data. 78 * Return 0 and fills in rtentry into @prt on success, 79 * Note: rtentry mask ptr will be set to @netmask , thus its pointer is required 80 * to be stable till the end of the operation (radix rt insertion/change/removal). 81 */ 82 struct rtentry * 83 rt_alloc(struct rib_head *rnh, const struct sockaddr *dst, 84 struct sockaddr *netmask) 85 { 86 MPASS(dst->sa_len <= sizeof(((struct rtentry *)NULL)->rt_dstb)); 87 88 struct rtentry *rt = uma_zalloc(V_rtzone, M_NOWAIT | M_ZERO); 89 if (rt == NULL) 90 return (NULL); 91 rt->rte_flags = RTF_UP | (netmask == NULL ? RTF_HOST : 0); 92 93 /* Fill in dst, ensuring it's masked if needed. */ 94 if (netmask != NULL) { 95 rt_maskedcopy(dst, &rt->rt_dst, netmask); 96 } else 97 bcopy(dst, &rt->rt_dst, dst->sa_len); 98 rt_key(rt) = &rt->rt_dst; 99 /* Set netmask to the storage from info. It will be updated upon insertion */ 100 rt_mask(rt) = netmask; 101 102 return (rt); 103 } 104 105 static void 106 destroy_rtentry(struct rtentry *rt) 107 { 108 #ifdef VIMAGE 109 struct nhop_object *nh = rt->rt_nhop; 110 111 /* 112 * At this moment rnh, nh_control may be already freed. 113 * nhop interface may have been migrated to a different vnet. 114 * Use vnet stored in the nexthop to delete the entry. 115 */ 116 #ifdef ROUTE_MPATH 117 if (NH_IS_NHGRP(nh)) { 118 const struct weightened_nhop *wn; 119 uint32_t num_nhops; 120 wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops); 121 nh = wn[0].nh; 122 } 123 #endif 124 CURVNET_SET(nhop_get_vnet(nh)); 125 #endif 126 127 /* Unreference nexthop */ 128 nhop_free_any(rt->rt_nhop); 129 130 rt_free_immediate(rt); 131 132 CURVNET_RESTORE(); 133 } 134 135 /* 136 * Epoch callback indicating rtentry is safe to destroy 137 */ 138 static void 139 destroy_rtentry_epoch(epoch_context_t ctx) 140 { 141 struct rtentry *rt; 142 143 rt = __containerof(ctx, struct rtentry, rt_epoch_ctx); 144 145 destroy_rtentry(rt); 146 } 147 148 /* 149 * Schedule rtentry deletion 150 */ 151 void 152 rt_free(struct rtentry *rt) 153 { 154 155 KASSERT(rt != NULL, ("%s: NULL rt", __func__)); 156 157 NET_EPOCH_CALL(destroy_rtentry_epoch, &rt->rt_epoch_ctx); 158 } 159 160 void 161 rt_free_immediate(struct rtentry *rt) 162 { 163 uma_zfree(V_rtzone, rt); 164 } 165 166 bool 167 rt_is_host(const struct rtentry *rt) 168 { 169 170 return (rt->rte_flags & RTF_HOST); 171 } 172 173 sa_family_t 174 rt_get_family(const struct rtentry *rt) 175 { 176 const struct sockaddr *dst; 177 178 dst = (const struct sockaddr *)rt_key_const(rt); 179 180 return (dst->sa_family); 181 } 182 183 /* 184 * Returns pointer to nexthop or nexthop group 185 * associated with @rt 186 */ 187 struct nhop_object * 188 rt_get_raw_nhop(const struct rtentry *rt) 189 { 190 191 return (rt->rt_nhop); 192 } 193 194 void 195 rt_get_rnd(const struct rtentry *rt, struct route_nhop_data *rnd) 196 { 197 rnd->rnd_nhop = rt->rt_nhop; 198 rnd->rnd_weight = rt->rt_weight; 199 } 200 201 /* 202 * If the process in in jail w/o VNET, export only host routes for the 203 * addresses assigned to the jail. 204 * Otherwise, allow exporting the entire table. 205 */ 206 bool 207 rt_is_exportable(const struct rtentry *rt, struct ucred *cred) 208 { 209 if (!rt_is_host(rt)) { 210 /* 211 * Performance optimisation: only host routes are allowed 212 * in the jail w/o vnet. 213 */ 214 if (jailed_without_vnet(cred)) 215 return (false); 216 } else { 217 if (prison_if(cred, rt_key_const(rt)) != 0) 218 return (false); 219 } 220 221 return (true); 222 } 223 224 #ifdef INET 225 /* 226 * Stores IPv4 address and prefix length of @rt inside 227 * @paddr and @plen. 228 * @pscopeid is currently always set to 0. 229 */ 230 void 231 rt_get_inet_prefix_plen(const struct rtentry *rt, struct in_addr *paddr, 232 int *plen, uint32_t *pscopeid) 233 { 234 const struct sockaddr_in *dst; 235 236 dst = (const struct sockaddr_in *)rt_key_const(rt); 237 KASSERT((dst->sin_family == AF_INET), 238 ("rt family is %d, not inet", dst->sin_family)); 239 *paddr = dst->sin_addr; 240 dst = (const struct sockaddr_in *)rt_mask_const(rt); 241 if (dst == NULL) 242 *plen = 32; 243 else 244 *plen = bitcount32(dst->sin_addr.s_addr); 245 *pscopeid = 0; 246 } 247 248 /* 249 * Stores IPv4 address and prefix mask of @rt inside 250 * @paddr and @pmask. Sets mask to INADDR_ANY for host routes. 251 * @pscopeid is currently always set to 0. 252 */ 253 void 254 rt_get_inet_prefix_pmask(const struct rtentry *rt, struct in_addr *paddr, 255 struct in_addr *pmask, uint32_t *pscopeid) 256 { 257 const struct sockaddr_in *dst; 258 259 dst = (const struct sockaddr_in *)rt_key_const(rt); 260 KASSERT((dst->sin_family == AF_INET), 261 ("rt family is %d, not inet", dst->sin_family)); 262 *paddr = dst->sin_addr; 263 dst = (const struct sockaddr_in *)rt_mask_const(rt); 264 if (dst == NULL) 265 pmask->s_addr = INADDR_BROADCAST; 266 else 267 *pmask = dst->sin_addr; 268 *pscopeid = 0; 269 } 270 #endif 271 272 #ifdef INET6 273 static int 274 inet6_get_plen(const struct in6_addr *addr) 275 { 276 277 return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) + 278 bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3])); 279 } 280 281 /* 282 * Stores IPv6 address and prefix length of @rt inside 283 * @paddr and @plen. Addresses are returned in de-embedded form. 284 * Scopeid is set to 0 for non-LL addresses. 285 */ 286 void 287 rt_get_inet6_prefix_plen(const struct rtentry *rt, struct in6_addr *paddr, 288 int *plen, uint32_t *pscopeid) 289 { 290 const struct sockaddr_in6 *dst; 291 292 dst = (const struct sockaddr_in6 *)rt_key_const(rt); 293 KASSERT((dst->sin6_family == AF_INET6), 294 ("rt family is %d, not inet6", dst->sin6_family)); 295 if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) 296 in6_splitscope(&dst->sin6_addr, paddr, pscopeid); 297 else 298 *paddr = dst->sin6_addr; 299 dst = (const struct sockaddr_in6 *)rt_mask_const(rt); 300 if (dst == NULL) 301 *plen = 128; 302 else 303 *plen = inet6_get_plen(&dst->sin6_addr); 304 } 305 306 /* 307 * Stores IPv6 address and prefix mask of @rt inside 308 * @paddr and @pmask. Addresses are returned in de-embedded form. 309 * Scopeid is set to 0 for non-LL addresses. 310 */ 311 void 312 rt_get_inet6_prefix_pmask(const struct rtentry *rt, struct in6_addr *paddr, 313 struct in6_addr *pmask, uint32_t *pscopeid) 314 { 315 const struct sockaddr_in6 *dst; 316 317 dst = (const struct sockaddr_in6 *)rt_key_const(rt); 318 KASSERT((dst->sin6_family == AF_INET6), 319 ("rt family is %d, not inet", dst->sin6_family)); 320 if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) 321 in6_splitscope(&dst->sin6_addr, paddr, pscopeid); 322 else 323 *paddr = dst->sin6_addr; 324 dst = (const struct sockaddr_in6 *)rt_mask_const(rt); 325 if (dst == NULL) 326 memset(pmask, 0xFF, sizeof(struct in6_addr)); 327 else 328 *pmask = dst->sin6_addr; 329 } 330 #endif 331 332 333