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_private.h> 50 #include <net/if_dl.h> 51 #include <net/route.h> 52 #include <net/route/route_ctl.h> 53 #include <net/route/route_var.h> 54 #include <net/route/nhop.h> 55 #include <net/vnet.h> 56 57 #include <netinet/in.h> 58 59 /* 60 * Control interface address fib propagation. 61 * By default, interface address routes are added to the fib of the interface. 62 * Once set to non-zero, adds interface address route to all fibs. 63 */ 64 VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0; 65 SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, 66 &VNET_NAME(rt_add_addr_allfibs), 0, ""); 67 68 /* 69 * Executes routing tables change specified by @cmd and @info for the fib 70 * @fibnum. Generates routing message on success. 71 * Note: it assumes there is only single route (interface route) for the 72 * provided prefix. 73 * Returns 0 on success or errno. 74 */ 75 static int 76 rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info) 77 { 78 struct rib_cmd_info rc; 79 struct nhop_object *nh; 80 int error; 81 82 error = rib_action(fibnum, cmd, info, &rc); 83 if (error == 0) { 84 if (cmd == RTM_ADD) 85 nh = nhop_select(rc.rc_nh_new, 0); 86 else 87 nh = nhop_select(rc.rc_nh_old, 0); 88 rt_routemsg(cmd, rc.rc_rt, nh, fibnum); 89 } 90 91 return (error); 92 } 93 94 /* 95 * Adds/deletes interface prefix specified by @info to the routing table. 96 * If V_rt_add_addr_allfibs is set, iterates over all existing routing 97 * tables, otherwise uses fib in @fibnum. Generates routing message for 98 * each table. 99 * Returns 0 on success or errno. 100 */ 101 int 102 rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info) 103 { 104 int error = 0, last_error = 0; 105 bool didwork = false; 106 107 if (V_rt_add_addr_allfibs == 0) { 108 error = rib_handle_ifaddr_one(fibnum, cmd, info); 109 didwork = (error == 0); 110 } else { 111 for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) { 112 error = rib_handle_ifaddr_one(fibnum, cmd, info); 113 if (error == 0) 114 didwork = true; 115 else 116 last_error = error; 117 } 118 } 119 120 if (cmd == RTM_DELETE) { 121 if (didwork) { 122 error = 0; 123 } else { 124 /* we only give an error if it wasn't in any table */ 125 error = ((info->rti_flags & RTF_HOST) ? 126 EHOSTUNREACH : ENETUNREACH); 127 } 128 } else { 129 if (last_error != 0) { 130 /* return an error if any of them failed */ 131 error = last_error; 132 } 133 } 134 return (error); 135 } 136 137 static int 138 ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa, 139 struct sockaddr *ia) 140 { 141 struct rib_cmd_info rc; 142 struct epoch_tracker et; 143 int error; 144 struct rt_addrinfo info; 145 struct sockaddr_dl null_sdl; 146 struct ifnet *ifp; 147 148 ifp = ifa->ifa_ifp; 149 150 NET_EPOCH_ENTER(et); 151 bzero(&info, sizeof(info)); 152 if (cmd != RTM_DELETE) 153 info.rti_ifp = V_loif; 154 if (cmd == RTM_ADD) { 155 /* explicitly specify (loopback) ifa */ 156 if (info.rti_ifp != NULL) 157 info.rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp); 158 } 159 info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED; 160 info.rti_info[RTAX_DST] = ia; 161 info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl; 162 link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type); 163 164 error = rib_action(ifp->if_fib, cmd, &info, &rc); 165 NET_EPOCH_EXIT(et); 166 167 if (error == 0 || 168 (cmd == RTM_ADD && error == EEXIST) || 169 (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH))) 170 return (error); 171 172 log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n", 173 __func__, otype, if_name(ifp), error); 174 175 return (error); 176 } 177 178 int 179 ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 180 { 181 182 return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia)); 183 } 184 185 int 186 ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 187 { 188 189 return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia)); 190 } 191 192 int 193 ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) 194 { 195 196 return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia)); 197 } 198 199 static bool 200 match_kernel_route(const struct rtentry *rt, struct nhop_object *nh) 201 { 202 if (!NH_IS_NHGRP(nh) && (nhop_get_rtflags(nh) & RTF_PINNED) && 203 nh->nh_aifp->if_fib == nhop_get_fibnum(nh)) 204 return (true); 205 return (false); 206 } 207 208 static int 209 pick_kernel_route(struct rtentry *rt, void *arg) 210 { 211 struct nhop_object *nh = rt->rt_nhop; 212 struct rib_head *rh_dst = (struct rib_head *)arg; 213 214 if (match_kernel_route(rt, nh)) { 215 struct rib_cmd_info rc = {}; 216 struct route_nhop_data rnd = { 217 .rnd_nhop = nh, 218 .rnd_weight = rt->rt_weight, 219 }; 220 rib_copy_route(rt, &rnd, rh_dst, &rc); 221 } 222 return (0); 223 } 224 225 /* 226 * Tries to copy kernel routes matching pattern from @rh_src to @rh_dst. 227 * 228 * Note: as this function acquires locks for both @rh_src and @rh_dst, 229 * it needs to be called under RTABLES_LOCK() to avoid deadlocking 230 * with multiple ribs. 231 */ 232 void 233 rib_copy_kernel_routes(struct rib_head *rh_src, struct rib_head *rh_dst) 234 { 235 struct epoch_tracker et; 236 237 if (V_rt_add_addr_allfibs == 0) 238 return; 239 240 NET_EPOCH_ENTER(et); 241 rib_walk_ext_internal(rh_src, false, pick_kernel_route, NULL, rh_dst); 242 NET_EPOCH_EXIT(et); 243 } 244 245