xref: /freebsd/sys/net/route/route_rtentry.c (revision d5b0e70f7e04d971691517ce1304d86a1e367e2e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/kernel.h>
39 #include <sys/lock.h>
40 #include <sys/rmlock.h>
41 
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <net/vnet.h>
45 #include <net/route.h>
46 #include <net/route/route_ctl.h>
47 #include <net/route/route_var.h>
48 #include <net/route/nhop.h>
49 #include <netinet/in.h>
50 #include <netinet6/scope6_var.h>
51 #include <netinet6/in6_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 	epoch_call(net_epoch_preempt, destroy_rtentry_epoch,
158 	    &rt->rt_epoch_ctx);
159 }
160 
161 void
162 rt_free_immediate(struct rtentry *rt)
163 {
164 	uma_zfree(V_rtzone, rt);
165 }
166 
167 bool
168 rt_is_host(const struct rtentry *rt)
169 {
170 
171 	return (rt->rte_flags & RTF_HOST);
172 }
173 
174 sa_family_t
175 rt_get_family(const struct rtentry *rt)
176 {
177 	const struct sockaddr *dst;
178 
179 	dst = (const struct sockaddr *)rt_key_const(rt);
180 
181 	return (dst->sa_family);
182 }
183 
184 /*
185  * Returns pointer to nexthop or nexthop group
186  * associated with @rt
187  */
188 struct nhop_object *
189 rt_get_raw_nhop(const struct rtentry *rt)
190 {
191 
192 	return (rt->rt_nhop);
193 }
194 
195 void
196 rt_get_rnd(const struct rtentry *rt, struct route_nhop_data *rnd)
197 {
198 	rnd->rnd_nhop = rt->rt_nhop;
199 	rnd->rnd_weight = rt->rt_weight;
200 }
201 
202 #ifdef INET
203 /*
204  * Stores IPv4 address and prefix length of @rt inside
205  *  @paddr and @plen.
206  * @pscopeid is currently always set to 0.
207  */
208 void
209 rt_get_inet_prefix_plen(const struct rtentry *rt, struct in_addr *paddr,
210     int *plen, uint32_t *pscopeid)
211 {
212 	const struct sockaddr_in *dst;
213 
214 	dst = (const struct sockaddr_in *)rt_key_const(rt);
215 	KASSERT((dst->sin_family == AF_INET),
216 	    ("rt family is %d, not inet", dst->sin_family));
217 	*paddr = dst->sin_addr;
218 	dst = (const struct sockaddr_in *)rt_mask_const(rt);
219 	if (dst == NULL)
220 		*plen = 32;
221 	else
222 		*plen = bitcount32(dst->sin_addr.s_addr);
223 	*pscopeid = 0;
224 }
225 
226 /*
227  * Stores IPv4 address and prefix mask of @rt inside
228  *  @paddr and @pmask. Sets mask to INADDR_ANY for host routes.
229  * @pscopeid is currently always set to 0.
230  */
231 void
232 rt_get_inet_prefix_pmask(const struct rtentry *rt, struct in_addr *paddr,
233     struct in_addr *pmask, uint32_t *pscopeid)
234 {
235 	const struct sockaddr_in *dst;
236 
237 	dst = (const struct sockaddr_in *)rt_key_const(rt);
238 	KASSERT((dst->sin_family == AF_INET),
239 	    ("rt family is %d, not inet", dst->sin_family));
240 	*paddr = dst->sin_addr;
241 	dst = (const struct sockaddr_in *)rt_mask_const(rt);
242 	if (dst == NULL)
243 		pmask->s_addr = INADDR_BROADCAST;
244 	else
245 		*pmask = dst->sin_addr;
246 	*pscopeid = 0;
247 }
248 #endif
249 
250 #ifdef INET6
251 static int
252 inet6_get_plen(const struct in6_addr *addr)
253 {
254 
255 	return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) +
256 	    bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3]));
257 }
258 
259 /*
260  * Stores IPv6 address and prefix length of @rt inside
261  *  @paddr and @plen. Addresses are returned in de-embedded form.
262  * Scopeid is set to 0 for non-LL addresses.
263  */
264 void
265 rt_get_inet6_prefix_plen(const struct rtentry *rt, struct in6_addr *paddr,
266     int *plen, uint32_t *pscopeid)
267 {
268 	const struct sockaddr_in6 *dst;
269 
270 	dst = (const struct sockaddr_in6 *)rt_key_const(rt);
271 	KASSERT((dst->sin6_family == AF_INET6),
272 	    ("rt family is %d, not inet6", dst->sin6_family));
273 	if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
274 		in6_splitscope(&dst->sin6_addr, paddr, pscopeid);
275 	else
276 		*paddr = dst->sin6_addr;
277 	dst = (const struct sockaddr_in6 *)rt_mask_const(rt);
278 	if (dst == NULL)
279 		*plen = 128;
280 	else
281 		*plen = inet6_get_plen(&dst->sin6_addr);
282 }
283 
284 /*
285  * Stores IPv6 address and prefix mask of @rt inside
286  *  @paddr and @pmask. Addresses are returned in de-embedded form.
287  * Scopeid is set to 0 for non-LL addresses.
288  */
289 void
290 rt_get_inet6_prefix_pmask(const struct rtentry *rt, struct in6_addr *paddr,
291     struct in6_addr *pmask, uint32_t *pscopeid)
292 {
293 	const struct sockaddr_in6 *dst;
294 
295 	dst = (const struct sockaddr_in6 *)rt_key_const(rt);
296 	KASSERT((dst->sin6_family == AF_INET6),
297 	    ("rt family is %d, not inet", dst->sin6_family));
298 	if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
299 		in6_splitscope(&dst->sin6_addr, paddr, pscopeid);
300 	else
301 		*paddr = dst->sin6_addr;
302 	dst = (const struct sockaddr_in6 *)rt_mask_const(rt);
303 	if (dst == NULL)
304 		memset(pmask, 0xFF, sizeof(struct in6_addr));
305 	else
306 		*pmask = dst->sin6_addr;
307 }
308 #endif
309 
310 
311