xref: /freebsd/sys/net/route/route_ifaddrs.c (revision 29363fb446372cb3f10bc98664e9767c53fbb457)
1770495f4SAlexander V. Chernikov /*-
2770495f4SAlexander V. Chernikov  * SPDX-License-Identifier: BSD-3-Clause
3770495f4SAlexander V. Chernikov  *
4770495f4SAlexander V. Chernikov  * Copyright (c) 1980, 1986, 1991, 1993
5770495f4SAlexander V. Chernikov  *	The Regents of the University of California.  All rights reserved.
6770495f4SAlexander V. Chernikov  *
7770495f4SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
8770495f4SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
9770495f4SAlexander V. Chernikov  * are met:
10770495f4SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
11770495f4SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
12770495f4SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
13770495f4SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
14770495f4SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
15770495f4SAlexander V. Chernikov  * 3. Neither the name of the University nor the names of its contributors
16770495f4SAlexander V. Chernikov  *    may be used to endorse or promote products derived from this software
17770495f4SAlexander V. Chernikov  *    without specific prior written permission.
18770495f4SAlexander V. Chernikov  *
19770495f4SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20770495f4SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21770495f4SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22770495f4SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23770495f4SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24770495f4SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25770495f4SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26770495f4SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27770495f4SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28770495f4SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29770495f4SAlexander V. Chernikov  * SUCH DAMAGE.
30770495f4SAlexander V. Chernikov  */
31770495f4SAlexander V. Chernikov 
32770495f4SAlexander V. Chernikov #include "opt_route.h"
33770495f4SAlexander V. Chernikov 
34770495f4SAlexander V. Chernikov #include <sys/param.h>
35770495f4SAlexander V. Chernikov #include <sys/systm.h>
36770495f4SAlexander V. Chernikov #include <sys/malloc.h>
37770495f4SAlexander V. Chernikov #include <sys/socket.h>
38770495f4SAlexander V. Chernikov #include <sys/sysctl.h>
39770495f4SAlexander V. Chernikov #include <sys/syslog.h>
40770495f4SAlexander V. Chernikov #include <sys/kernel.h>
41770495f4SAlexander V. Chernikov #include <sys/lock.h>
42770495f4SAlexander V. Chernikov #include <sys/rmlock.h>
43770495f4SAlexander V. Chernikov 
44770495f4SAlexander V. Chernikov #include <net/if.h>
45770495f4SAlexander V. Chernikov #include <net/if_var.h>
46*2c2b37adSJustin Hibbits #include <net/if_private.h>
47770495f4SAlexander V. Chernikov #include <net/if_dl.h>
48770495f4SAlexander V. Chernikov #include <net/route.h>
49770495f4SAlexander V. Chernikov #include <net/route/route_ctl.h>
50770495f4SAlexander V. Chernikov #include <net/route/route_var.h>
51770495f4SAlexander V. Chernikov #include <net/route/nhop.h>
52770495f4SAlexander V. Chernikov #include <net/vnet.h>
53770495f4SAlexander V. Chernikov 
54770495f4SAlexander V. Chernikov #include <netinet/in.h>
55770495f4SAlexander V. Chernikov 
56770495f4SAlexander V. Chernikov /*
57770495f4SAlexander V. Chernikov  * Control interface address fib propagation.
58770495f4SAlexander V. Chernikov  * By default, interface address routes are added to the fib of the interface.
59770495f4SAlexander V. Chernikov  * Once set to non-zero, adds interface address route to all fibs.
60770495f4SAlexander V. Chernikov  */
612d398241SAlexander V. Chernikov VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0;
62770495f4SAlexander V. Chernikov SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET,
63770495f4SAlexander V. Chernikov     &VNET_NAME(rt_add_addr_allfibs), 0, "");
64770495f4SAlexander V. Chernikov 
657b3440fcSAlexander V. Chernikov /*
667b3440fcSAlexander V. Chernikov  * Executes routing tables change specified by @cmd and @info for the fib
677b3440fcSAlexander V. Chernikov  * @fibnum. Generates routing message on success.
687b3440fcSAlexander V. Chernikov  * Note: it assumes there is only single route (interface route) for the
697b3440fcSAlexander V. Chernikov  * provided prefix.
707b3440fcSAlexander V. Chernikov  * Returns 0 on success or errno.
717b3440fcSAlexander V. Chernikov  */
727b3440fcSAlexander V. Chernikov static int
rib_handle_ifaddr_one(uint32_t fibnum,int cmd,struct rt_addrinfo * info)737b3440fcSAlexander V. Chernikov rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
74770495f4SAlexander V. Chernikov {
757b3440fcSAlexander V. Chernikov 	struct rib_cmd_info rc;
7681728a53SAlexander V. Chernikov 	struct nhop_object *nh;
777b3440fcSAlexander V. Chernikov 	int error;
78770495f4SAlexander V. Chernikov 
797b3440fcSAlexander V. Chernikov 	error = rib_action(fibnum, cmd, info, &rc);
807b3440fcSAlexander V. Chernikov 	if (error == 0) {
817b3440fcSAlexander V. Chernikov 		if (cmd == RTM_ADD)
827b3440fcSAlexander V. Chernikov 			nh = nhop_select(rc.rc_nh_new, 0);
8381728a53SAlexander V. Chernikov 		else
847b3440fcSAlexander V. Chernikov 			nh = nhop_select(rc.rc_nh_old, 0);
857b3440fcSAlexander V. Chernikov 		rt_routemsg(cmd, rc.rc_rt, nh, fibnum);
86a1b59379SAlexander V. Chernikov 	}
87a1b59379SAlexander V. Chernikov 
887b3440fcSAlexander V. Chernikov 	return (error);
897b3440fcSAlexander V. Chernikov }
907b3440fcSAlexander V. Chernikov 
917b3440fcSAlexander V. Chernikov /*
927b3440fcSAlexander V. Chernikov  * Adds/deletes interface prefix specified by @info to the routing table.
937b3440fcSAlexander V. Chernikov  * If V_rt_add_addr_allfibs is set, iterates over all existing routing
947b3440fcSAlexander V. Chernikov  * tables, otherwise uses fib in @fibnum. Generates routing message for
957b3440fcSAlexander V. Chernikov  *  each table.
967b3440fcSAlexander V. Chernikov  * Returns 0 on success or errno.
977b3440fcSAlexander V. Chernikov  */
98a1b59379SAlexander V. Chernikov int
rib_handle_ifaddr_info(uint32_t fibnum,int cmd,struct rt_addrinfo * info)997b3440fcSAlexander V. Chernikov rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
100a1b59379SAlexander V. Chernikov {
1017b3440fcSAlexander V. Chernikov 	int error = 0, last_error = 0;
1027b3440fcSAlexander V. Chernikov 	bool didwork = false;
103a1b59379SAlexander V. Chernikov 
1047b3440fcSAlexander V. Chernikov 	if (V_rt_add_addr_allfibs == 0) {
1057b3440fcSAlexander V. Chernikov 		error = rib_handle_ifaddr_one(fibnum, cmd, info);
1067b3440fcSAlexander V. Chernikov 		didwork = (error == 0);
1077b3440fcSAlexander V. Chernikov 	} else {
1087b3440fcSAlexander V. Chernikov 		for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) {
1097b3440fcSAlexander V. Chernikov 			error = rib_handle_ifaddr_one(fibnum, cmd, info);
1107b3440fcSAlexander V. Chernikov 			if (error == 0)
1117b3440fcSAlexander V. Chernikov 				didwork = true;
1127b3440fcSAlexander V. Chernikov 			else
1137b3440fcSAlexander V. Chernikov 				last_error = error;
114770495f4SAlexander V. Chernikov 		}
115770495f4SAlexander V. Chernikov 	}
116770495f4SAlexander V. Chernikov 
1177b3440fcSAlexander V. Chernikov 	if (cmd == RTM_DELETE) {
1187b3440fcSAlexander V. Chernikov 		if (didwork) {
1197b3440fcSAlexander V. Chernikov 			error = 0;
1207b3440fcSAlexander V. Chernikov 		} else {
1217b3440fcSAlexander V. Chernikov 			/* we only give an error if it wasn't in any table */
1227b3440fcSAlexander V. Chernikov 			error = ((info->rti_flags & RTF_HOST) ?
1237b3440fcSAlexander V. Chernikov 			    EHOSTUNREACH : ENETUNREACH);
1247b3440fcSAlexander V. Chernikov 		}
1257b3440fcSAlexander V. Chernikov 	} else {
1267b3440fcSAlexander V. Chernikov 		if (last_error != 0) {
1277b3440fcSAlexander V. Chernikov 			/* return an error if any of them failed */
1287b3440fcSAlexander V. Chernikov 			error = last_error;
1297b3440fcSAlexander V. Chernikov 		}
1307b3440fcSAlexander V. Chernikov 	}
131770495f4SAlexander V. Chernikov 	return (error);
132770495f4SAlexander V. Chernikov }
133770495f4SAlexander V. Chernikov 
134770495f4SAlexander V. Chernikov static int
ifa_maintain_loopback_route(int cmd,const char * otype,struct ifaddr * ifa,struct sockaddr * ia)1357b3440fcSAlexander V. Chernikov ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa,
1367b3440fcSAlexander V. Chernikov     struct sockaddr *ia)
137770495f4SAlexander V. Chernikov {
138770495f4SAlexander V. Chernikov 	struct rib_cmd_info rc;
1397b3440fcSAlexander V. Chernikov 	struct epoch_tracker et;
140770495f4SAlexander V. Chernikov 	int error;
1417b3440fcSAlexander V. Chernikov 	struct rt_addrinfo info;
1427b3440fcSAlexander V. Chernikov 	struct sockaddr_dl null_sdl;
1437b3440fcSAlexander V. Chernikov 	struct ifnet *ifp;
144770495f4SAlexander V. Chernikov 
1457b3440fcSAlexander V. Chernikov 	ifp = ifa->ifa_ifp;
146770495f4SAlexander V. Chernikov 
1477b3440fcSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
1487b3440fcSAlexander V. Chernikov 	bzero(&info, sizeof(info));
1497b3440fcSAlexander V. Chernikov 	if (cmd != RTM_DELETE)
1507b3440fcSAlexander V. Chernikov 		info.rti_ifp = V_loif;
1517b3440fcSAlexander V. Chernikov 	if (cmd == RTM_ADD) {
1527b3440fcSAlexander V. Chernikov 		/* explicitly specify (loopback) ifa */
1537b3440fcSAlexander V. Chernikov 		if (info.rti_ifp != NULL)
1547b3440fcSAlexander V. Chernikov 			info.rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp);
1557b3440fcSAlexander V. Chernikov 	}
1567b3440fcSAlexander V. Chernikov 	info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED;
1577b3440fcSAlexander V. Chernikov 	info.rti_info[RTAX_DST] = ia;
1587b3440fcSAlexander V. Chernikov 	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl;
1597b3440fcSAlexander V. Chernikov 	link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type);
160770495f4SAlexander V. Chernikov 
1617b3440fcSAlexander V. Chernikov 	error = rib_action(ifp->if_fib, cmd, &info, &rc);
1627b3440fcSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
1637b3440fcSAlexander V. Chernikov 
1647b3440fcSAlexander V. Chernikov 	if (error == 0 ||
1657b3440fcSAlexander V. Chernikov 	    (cmd == RTM_ADD && error == EEXIST) ||
1667b3440fcSAlexander V. Chernikov 	    (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH)))
167770495f4SAlexander V. Chernikov 		return (error);
168770495f4SAlexander V. Chernikov 
1697b3440fcSAlexander V. Chernikov 	log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n",
1707b3440fcSAlexander V. Chernikov 		__func__, otype, if_name(ifp), error);
171770495f4SAlexander V. Chernikov 
172770495f4SAlexander V. Chernikov 	return (error);
173770495f4SAlexander V. Chernikov }
174770495f4SAlexander V. Chernikov 
175770495f4SAlexander V. Chernikov int
ifa_add_loopback_route(struct ifaddr * ifa,struct sockaddr * ia)176770495f4SAlexander V. Chernikov ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
177770495f4SAlexander V. Chernikov {
178770495f4SAlexander V. Chernikov 
1797b3440fcSAlexander V. Chernikov 	return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia));
180a1b59379SAlexander V. Chernikov }
181a1b59379SAlexander V. Chernikov 
182a1b59379SAlexander V. Chernikov int
ifa_del_loopback_route(struct ifaddr * ifa,struct sockaddr * ia)183a1b59379SAlexander V. Chernikov ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
184a1b59379SAlexander V. Chernikov {
185a1b59379SAlexander V. Chernikov 
1867b3440fcSAlexander V. Chernikov 	return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia));
1877b3440fcSAlexander V. Chernikov }
188a1b59379SAlexander V. Chernikov 
1897b3440fcSAlexander V. Chernikov int
ifa_switch_loopback_route(struct ifaddr * ifa,struct sockaddr * ia)1907b3440fcSAlexander V. Chernikov ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
1917b3440fcSAlexander V. Chernikov {
192a1b59379SAlexander V. Chernikov 
1937b3440fcSAlexander V. Chernikov 	return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia));
194770495f4SAlexander V. Chernikov }
195770495f4SAlexander V. Chernikov 
19640503b79SAlexander V. Chernikov static bool
match_kernel_route(const struct rtentry * rt,struct nhop_object * nh)19740503b79SAlexander V. Chernikov match_kernel_route(const struct rtentry *rt, struct nhop_object *nh)
19840503b79SAlexander V. Chernikov {
19940503b79SAlexander V. Chernikov 	if (!NH_IS_NHGRP(nh) && (nhop_get_rtflags(nh) & RTF_PINNED) &&
20040503b79SAlexander V. Chernikov 	    nh->nh_aifp->if_fib == nhop_get_fibnum(nh))
20140503b79SAlexander V. Chernikov 		return (true);
20240503b79SAlexander V. Chernikov 	return (false);
20340503b79SAlexander V. Chernikov }
20440503b79SAlexander V. Chernikov 
20540503b79SAlexander V. Chernikov static int
pick_kernel_route(struct rtentry * rt,void * arg)20640503b79SAlexander V. Chernikov pick_kernel_route(struct rtentry *rt, void *arg)
20740503b79SAlexander V. Chernikov {
20840503b79SAlexander V. Chernikov 	struct nhop_object *nh = rt->rt_nhop;
20940503b79SAlexander V. Chernikov 	struct rib_head *rh_dst = (struct rib_head *)arg;
21040503b79SAlexander V. Chernikov 
21140503b79SAlexander V. Chernikov 	if (match_kernel_route(rt, nh)) {
21240503b79SAlexander V. Chernikov 		struct rib_cmd_info rc = {};
21340503b79SAlexander V. Chernikov 		struct route_nhop_data rnd = {
21440503b79SAlexander V. Chernikov 			.rnd_nhop = nh,
21540503b79SAlexander V. Chernikov 			.rnd_weight = rt->rt_weight,
21640503b79SAlexander V. Chernikov 		};
21740503b79SAlexander V. Chernikov 		rib_copy_route(rt, &rnd, rh_dst, &rc);
21840503b79SAlexander V. Chernikov 	}
21940503b79SAlexander V. Chernikov 	return (0);
22040503b79SAlexander V. Chernikov }
22140503b79SAlexander V. Chernikov 
22240503b79SAlexander V. Chernikov /*
22340503b79SAlexander V. Chernikov  * Tries to copy kernel routes matching pattern from @rh_src to @rh_dst.
22440503b79SAlexander V. Chernikov  *
22540503b79SAlexander V. Chernikov  * Note: as this function acquires locks for both @rh_src and @rh_dst,
22640503b79SAlexander V. Chernikov  *  it needs to be called under RTABLES_LOCK() to avoid deadlocking
22740503b79SAlexander V. Chernikov  * with multiple ribs.
22840503b79SAlexander V. Chernikov  */
22940503b79SAlexander V. Chernikov void
rib_copy_kernel_routes(struct rib_head * rh_src,struct rib_head * rh_dst)23040503b79SAlexander V. Chernikov rib_copy_kernel_routes(struct rib_head *rh_src, struct rib_head *rh_dst)
23140503b79SAlexander V. Chernikov {
23240503b79SAlexander V. Chernikov 	struct epoch_tracker et;
23340503b79SAlexander V. Chernikov 
23440503b79SAlexander V. Chernikov 	if (V_rt_add_addr_allfibs == 0)
23540503b79SAlexander V. Chernikov 		return;
23640503b79SAlexander V. Chernikov 
23740503b79SAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
23840503b79SAlexander V. Chernikov 	rib_walk_ext_internal(rh_src, false, pick_kernel_route, NULL, rh_dst);
23940503b79SAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
24040503b79SAlexander V. Chernikov }
241770495f4SAlexander V. Chernikov 
242