1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1986, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)route.c 8.3.1.1 (Berkeley) 2/23/95 32 * $FreeBSD$ 33 */ 34 35 #include "opt_route.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/malloc.h> 40 #include <sys/socket.h> 41 #include <sys/sysctl.h> 42 #include <sys/syslog.h> 43 #include <sys/kernel.h> 44 #include <sys/lock.h> 45 #include <sys/rmlock.h> 46 47 #include <net/if.h> 48 #include <net/if_var.h> 49 #include <net/if_dl.h> 50 #include <net/route.h> 51 #include <net/route/route_ctl.h> 52 #include <net/route/route_var.h> 53 #include <net/route/nhop.h> 54 #include <net/vnet.h> 55 56 #include <netinet/in.h> 57 58 /* 59 * Control interface address fib propagation. 60 * By default, interface address routes are added to the fib of the interface. 61 * Once set to non-zero, adds interface address route to all fibs. 62 */ 63 VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0; 64 SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, 65 &VNET_NAME(rt_add_addr_allfibs), 0, ""); 66 67 static void 68 report_operation(uint32_t fibnum, struct rib_cmd_info *rc) 69 { 70 struct nhop_object *nh; 71 72 if (rc->rc_cmd == RTM_DELETE) 73 nh = nhop_select(rc->rc_nh_old, 0); 74 else 75 nh = nhop_select(rc->rc_nh_new, 0); 76 rt_routemsg(rc->rc_cmd, rc->rc_rt, nh, fibnum); 77 } 78 79 int 80 rib_add_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, 81 struct route_nhop_data *rnd, int op_flags) 82 { 83 struct rib_cmd_info rc = {}; 84 85 NET_EPOCH_ASSERT(); 86 87 int error = rib_add_route_px(fibnum, dst, plen, rnd, op_flags, &rc); 88 if (error != 0) 89 return (error); 90 report_operation(fibnum, &rc); 91 92 if (V_rt_add_addr_allfibs != 0) { 93 for (int i = 0; i < V_rt_numfibs; i++) { 94 if (i == fibnum) 95 continue; 96 struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 97 /* Don't care much about the errors in non-primary fib */ 98 if (rnh != NULL) { 99 if (rib_copy_route(rc.rc_rt, rnd, rnh, &rc) == 0) 100 report_operation(i, &rc); 101 } 102 } 103 } 104 105 return (error); 106 } 107 108 int 109 rib_del_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, 110 rib_filter_f_t *filter_func, void *filter_arg, int op_flags) 111 { 112 struct rib_cmd_info rc = {}; 113 114 NET_EPOCH_ASSERT(); 115 116 int error = rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg, 117 op_flags, &rc); 118 if (error != 0) 119 return (error); 120 report_operation(fibnum, &rc); 121 122 if (V_rt_add_addr_allfibs != 0) { 123 for (int i = 0; i < V_rt_numfibs; i++) { 124 if (i == fibnum) 125 continue; 126 /* Don't care much about the errors in non-primary fib */ 127 if (rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg, 128 op_flags, &rc) == 0) 129 report_operation(i, &rc); 130 } 131 } 132 133 return (error); 134 } 135 136 static int 137 add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags) 138 { 139 struct rib_cmd_info rc; 140 int error; 141 142 NET_EPOCH_ASSERT(); 143 144 struct ifnet *ifp = ifa->ifa_ifp; 145 struct nhop_object *nh = nhop_alloc(ifp->if_fib, ia->sa_family); 146 struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; 147 if (nh == NULL) 148 return (ENOMEM); 149 150 nhop_set_direct_gw(nh, ifp); 151 nhop_set_transmit_ifp(nh, V_loif); 152 nhop_set_src(nh, ifaof_ifpforaddr(ifa->ifa_addr, ifp)); 153 nhop_set_pinned(nh, true); 154 nhop_set_rtflags(nh, RTF_STATIC); 155 nhop_set_pxtype_flag(nh, NHF_HOST); 156 rnd.rnd_nhop = nhop_get_nhop(nh, &error); 157 if (error != 0) 158 return (error); 159 error = rib_add_route_px(ifp->if_fib, ia, -1, &rnd, op_flags, &rc); 160 161 if (error != 0) 162 log(LOG_DEBUG, "%s: failed to update interface %s route: %u\n", 163 __func__, if_name(ifp), error); 164 165 return (error); 166 } 167 168 int 169 ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 170 { 171 struct epoch_tracker et; 172 173 NET_EPOCH_ENTER(et); 174 int error = add_loopback_route_flags(ifa, ia, RTM_F_CREATE | RTM_F_FORCE); 175 NET_EPOCH_EXIT(et); 176 177 return (error); 178 } 179 180 int 181 ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 182 { 183 struct epoch_tracker et; 184 185 NET_EPOCH_ENTER(et); 186 int error = add_loopback_route_flags(ifa, ia, RTM_F_REPLACE | RTM_F_FORCE); 187 NET_EPOCH_EXIT(et); 188 189 return (error); 190 } 191 192 int 193 ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 194 { 195 struct ifnet *ifp = ifa->ifa_ifp; 196 struct sockaddr_dl link_sdl; 197 struct sockaddr *gw = (struct sockaddr *)&link_sdl; 198 struct rib_cmd_info rc; 199 struct epoch_tracker et; 200 int error; 201 202 NET_EPOCH_ENTER(et); 203 204 link_init_sdl(ifp, gw, ifp->if_type); 205 error = rib_del_route_px_gw(ifp->if_fib, ia, -1, gw, RTM_F_FORCE, &rc); 206 207 NET_EPOCH_EXIT(et); 208 209 if (error != 0) 210 log(LOG_DEBUG, "%s: failed to delete interface %s route: %u\n", 211 __func__, if_name(ifp), error); 212 213 return (error); 214 } 215 216 static bool 217 match_kernel_route(const struct rtentry *rt, struct nhop_object *nh) 218 { 219 if (!NH_IS_NHGRP(nh) && (nhop_get_rtflags(nh) & RTF_PINNED) && 220 nh->nh_aifp->if_fib == nhop_get_fibnum(nh)) 221 return (true); 222 return (false); 223 } 224 225 static int 226 pick_kernel_route(struct rtentry *rt, void *arg) 227 { 228 struct nhop_object *nh = rt->rt_nhop; 229 struct rib_head *rh_dst = (struct rib_head *)arg; 230 231 if (match_kernel_route(rt, nh)) { 232 struct rib_cmd_info rc = {}; 233 struct route_nhop_data rnd = { 234 .rnd_nhop = nh, 235 .rnd_weight = rt->rt_weight, 236 }; 237 rib_copy_route(rt, &rnd, rh_dst, &rc); 238 } 239 return (0); 240 } 241 242 /* 243 * Tries to copy kernel routes matching pattern from @rh_src to @rh_dst. 244 * 245 * Note: as this function acquires locks for both @rh_src and @rh_dst, 246 * it needs to be called under RTABLES_LOCK() to avoid deadlocking 247 * with multiple ribs. 248 */ 249 void 250 rib_copy_kernel_routes(struct rib_head *rh_src, struct rib_head *rh_dst) 251 { 252 struct epoch_tracker et; 253 254 if (V_rt_add_addr_allfibs == 0) 255 return; 256 257 NET_EPOCH_ENTER(et); 258 rib_walk_ext_internal(rh_src, false, pick_kernel_route, NULL, rh_dst); 259 NET_EPOCH_EXIT(et); 260 } 261 262