xref: /freebsd/sys/net/route/route_ifaddrs.c (revision 3cbb4cc200f8a0ad7ed08233425ea54524a21f1c)
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_mpath.h"
36 #include "opt_route.h"
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/socket.h>
42 #include <sys/sysctl.h>
43 #include <sys/syslog.h>
44 #include <sys/kernel.h>
45 #include <sys/lock.h>
46 #include <sys/rmlock.h>
47 
48 #include <net/if.h>
49 #include <net/if_var.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  * Set up a routing table entry, normally
70  * for an interface.
71  */
72 static inline  int
73 rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum)
74 {
75 	RIB_RLOCK_TRACKER;
76 	struct epoch_tracker et;
77 	struct sockaddr *dst;
78 	struct sockaddr *netmask;
79 	struct rib_cmd_info rc;
80 	struct rt_addrinfo info;
81 	int error = 0;
82 	int startfib, endfib;
83 	struct sockaddr_storage ss;
84 	int didwork = 0;
85 	int a_failure = 0;
86 	struct sockaddr_dl_short sdl;
87 	struct rib_head *rnh;
88 
89 	if (flags & RTF_HOST) {
90 		dst = ifa->ifa_dstaddr;
91 		netmask = NULL;
92 	} else {
93 		dst = ifa->ifa_addr;
94 		netmask = ifa->ifa_netmask;
95 	}
96 	if (dst->sa_len == 0)
97 		return(EINVAL);
98 	switch (dst->sa_family) {
99 	case AF_INET6:
100 	case AF_INET:
101 		/* We support multiple FIBs. */
102 		break;
103 	default:
104 		fibnum = RT_DEFAULT_FIB;
105 		break;
106 	}
107 	if (fibnum == RT_ALL_FIBS) {
108 		if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD)
109 			startfib = endfib = ifa->ifa_ifp->if_fib;
110 		else {
111 			startfib = 0;
112 			endfib = rt_numfibs - 1;
113 		}
114 	} else {
115 		KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum"));
116 		startfib = fibnum;
117 		endfib = fibnum;
118 	}
119 
120 	/*
121 	 * If it's a delete, check that if it exists,
122 	 * it's on the correct interface or we might scrub
123 	 * a route to another ifa which would
124 	 * be confusing at best and possibly worse.
125 	 */
126 	if (cmd == RTM_DELETE) {
127 		/*
128 		 * It's a delete, so it should already exist..
129 		 * If it's a net, mask off the host bits
130 		 * (Assuming we have a mask)
131 		 * XXX this is kinda inet specific..
132 		 */
133 		if (netmask != NULL) {
134 			rt_maskedcopy(dst, (struct sockaddr *)&ss, netmask);
135 			dst = (struct sockaddr *)&ss;
136 		}
137 	}
138 	bzero(&sdl, sizeof(struct sockaddr_dl_short));
139 	sdl.sdl_family = AF_LINK;
140 	sdl.sdl_len = sizeof(struct sockaddr_dl_short);
141 	sdl.sdl_type = ifa->ifa_ifp->if_type;
142 	sdl.sdl_index = ifa->ifa_ifp->if_index;
143 	/*
144 	 * Now go through all the requested tables (fibs) and do the
145 	 * requested action. Realistically, this will either be fib 0
146 	 * for protocols that don't do multiple tables or all the
147 	 * tables for those that do.
148 	 */
149 	for ( fibnum = startfib; fibnum <= endfib; fibnum++) {
150 		if (cmd == RTM_DELETE) {
151 			struct radix_node *rn;
152 			/*
153 			 * Look up an rtentry that is in the routing tree and
154 			 * contains the correct info.
155 			 */
156 			rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
157 			if (rnh == NULL)
158 				/* this table doesn't exist but others might */
159 				continue;
160 			RIB_RLOCK(rnh);
161 			rn = rnh->rnh_lookup(dst, netmask, &rnh->head);
162 			error = (rn == NULL ||
163 			    (rn->rn_flags & RNF_ROOT) ||
164 			    RNTORT(rn)->rt_nhop->nh_ifa != ifa);
165 			RIB_RUNLOCK(rnh);
166 			if (error) {
167 				/* this is only an error if bad on ALL tables */
168 				continue;
169 			}
170 		}
171 		/*
172 		 * Do the actual request
173 		 */
174 		bzero((caddr_t)&info, sizeof(info));
175 		info.rti_ifa = ifa;
176 		info.rti_flags = flags |
177 		    (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED;
178 		info.rti_info[RTAX_DST] = dst;
179 		info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl;
180 		info.rti_info[RTAX_NETMASK] = netmask;
181 		NET_EPOCH_ENTER(et);
182 		error = rib_action(fibnum, cmd, &info, &rc);
183 		if (error == 0 && rc.rc_rt != NULL) {
184 			/*
185 			 * notify any listening routing agents of the change
186 			 */
187 
188 			/* TODO: interface routes/aliases */
189 			rt_newaddrmsg_fib(cmd, ifa, rc.rc_rt, fibnum);
190 			didwork = 1;
191 		}
192 		NET_EPOCH_EXIT(et);
193 		if (error)
194 			a_failure = error;
195 	}
196 	if (cmd == RTM_DELETE) {
197 		if (didwork) {
198 			error = 0;
199 		} else {
200 			/* we only give an error if it wasn't in any table */
201 			error = ((flags & RTF_HOST) ?
202 			    EHOSTUNREACH : ENETUNREACH);
203 		}
204 	} else {
205 		if (a_failure) {
206 			/* return an error if any of them failed */
207 			error = a_failure;
208 		}
209 	}
210 	return (error);
211 }
212 
213 /*
214  * Set up a routing table entry, normally
215  * for an interface.
216  */
217 int
218 rtinit(struct ifaddr *ifa, int cmd, int flags)
219 {
220 	struct sockaddr *dst;
221 	int fib = RT_DEFAULT_FIB;
222 
223 	if (flags & RTF_HOST) {
224 		dst = ifa->ifa_dstaddr;
225 	} else {
226 		dst = ifa->ifa_addr;
227 	}
228 
229 	switch (dst->sa_family) {
230 	case AF_INET6:
231 	case AF_INET:
232 		/* We do support multiple FIBs. */
233 		fib = RT_ALL_FIBS;
234 		break;
235 	}
236 	return (rtinit1(ifa, cmd, flags, fib));
237 }
238 
239 static int
240 ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa,
241     struct sockaddr *ia)
242 {
243 	struct rib_cmd_info rc;
244 	struct epoch_tracker et;
245 	int error;
246 	struct rt_addrinfo info;
247 	struct sockaddr_dl null_sdl;
248 	struct ifnet *ifp;
249 	struct ifaddr *rti_ifa = NULL;
250 
251 	ifp = ifa->ifa_ifp;
252 
253 	NET_EPOCH_ENTER(et);
254 	bzero(&info, sizeof(info));
255 	if (cmd != RTM_DELETE)
256 		info.rti_ifp = V_loif;
257 	if (cmd == RTM_ADD) {
258 		/* explicitly specify (loopback) ifa */
259 		if (info.rti_ifp != NULL) {
260 			rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp);
261 			if (rti_ifa != NULL)
262 				ifa_ref(rti_ifa);
263 			info.rti_ifa = rti_ifa;
264 		}
265 	}
266 	info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED;
267 	info.rti_info[RTAX_DST] = ia;
268 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl;
269 	link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type);
270 
271 	error = rib_action(ifp->if_fib, cmd, &info, &rc);
272 	NET_EPOCH_EXIT(et);
273 
274 	if (rti_ifa != NULL)
275 		ifa_free(rti_ifa);
276 
277 	if (error == 0 ||
278 	    (cmd == RTM_ADD && error == EEXIST) ||
279 	    (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH)))
280 		return (error);
281 
282 	log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n",
283 		__func__, otype, if_name(ifp), error);
284 
285 	return (error);
286 }
287 
288 int
289 ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
290 {
291 
292 	return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia));
293 }
294 
295 int
296 ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
297 {
298 
299 	return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia));
300 }
301 
302 int
303 ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
304 {
305 
306 	return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia));
307 }
308 
309 
310