Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netinet6/in6_fib.c
104874 views
1
/*-
2
* Copyright (c) 2015
3
* Alexander V. Chernikov <[email protected]>
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
* 3. Neither the name of the University nor the names of its contributors
14
* may be used to endorse or promote products derived from this software
15
* without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
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/lock.h>
37
#include <sys/rmlock.h>
38
#include <sys/malloc.h>
39
#include <sys/mbuf.h>
40
#include <sys/socket.h>
41
#include <sys/sysctl.h>
42
#include <sys/kernel.h>
43
44
#include <net/if.h>
45
#include <net/if_var.h>
46
#include <net/if_dl.h>
47
#include <net/if_private.h>
48
#include <net/route.h>
49
#include <net/route/route_ctl.h>
50
#include <net/route/route_var.h>
51
#include <net/route/fib_algo.h>
52
#include <net/route/nhop.h>
53
#include <net/toeplitz.h>
54
#include <net/vnet.h>
55
56
#include <netinet/in.h>
57
#include <netinet/in_var.h>
58
#include <netinet/ip_mroute.h>
59
#include <netinet/ip6.h>
60
#include <netinet6/in6_fib.h>
61
#include <netinet6/in6_var.h>
62
#include <netinet6/nd6.h>
63
#include <netinet6/scope6_var.h>
64
65
#include <net/if_types.h>
66
67
#ifdef INET6
68
69
CHK_STRUCT_ROUTE_COMPAT(struct route_in6, ro_dst);
70
71
#ifdef FIB_ALGO
72
VNET_DEFINE(struct fib_dp *, inet6_dp);
73
#endif
74
75
#ifdef ROUTE_MPATH
76
struct _hash_5tuple_ipv6 {
77
struct in6_addr src;
78
struct in6_addr dst;
79
unsigned short src_port;
80
unsigned short dst_port;
81
char proto;
82
char spare[3];
83
};
84
_Static_assert(sizeof(struct _hash_5tuple_ipv6) == 40,
85
"_hash_5tuple_ipv6 size is wrong");
86
87
uint32_t
88
fib6_calc_software_hash(const struct in6_addr *src, const struct in6_addr *dst,
89
unsigned short src_port, unsigned short dst_port, char proto,
90
uint32_t *phashtype)
91
{
92
struct _hash_5tuple_ipv6 data;
93
94
data.src = *src;
95
data.dst = *dst;
96
data.src_port = src_port;
97
data.dst_port = dst_port;
98
data.proto = proto;
99
data.spare[0] = data.spare[1] = data.spare[2] = 0;
100
101
*phashtype = M_HASHTYPE_OPAQUE_HASH;
102
103
return (toeplitz_hash(MPATH_ENTROPY_KEY_LEN, mpath_entropy_key,
104
sizeof(data), (uint8_t *)&data));
105
}
106
#endif
107
108
/*
109
* Looks up path in fib @fibnum specified by @dst.
110
* Assumes scope is deembedded and provided in @scopeid.
111
*
112
* Returns path nexthop on success. Nexthop is safe to use
113
* within the current network epoch. If longer lifetime is required,
114
* one needs to pass NHR_REF as a flag. This will return referenced
115
* nexthop.
116
*/
117
#ifdef FIB_ALGO
118
struct nhop_object *
119
fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6,
120
uint32_t scopeid, uint32_t flags, uint32_t flowid)
121
{
122
struct nhop_object *nh;
123
struct fib_dp *dp = &V_inet6_dp[fibnum];
124
struct flm_lookup_key key = {.addr6 = dst6 };
125
126
nh = dp->f(dp->arg, key, scopeid);
127
if (nh != NULL) {
128
nh = nhop_select(nh, flowid);
129
/* Ensure route & ifp is UP */
130
if (RT_LINK_IS_UP(nh->nh_ifp)) {
131
if (flags & NHR_REF)
132
nhop_ref_object(nh);
133
return (nh);
134
}
135
}
136
RTSTAT_INC(rts_unreach);
137
return (NULL);
138
}
139
#else
140
struct nhop_object *
141
fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6,
142
uint32_t scopeid, uint32_t flags, uint32_t flowid)
143
{
144
RIB_RLOCK_TRACKER;
145
struct rib_head *rh;
146
struct radix_node *rn;
147
struct nhop_object *nh;
148
149
KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum"));
150
rh = rt_tables_get_rnh(fibnum, AF_INET6);
151
if (rh == NULL)
152
return (NULL);
153
154
struct sockaddr_in6 sin6 = {
155
.sin6_len = sizeof(struct sockaddr_in6),
156
.sin6_addr = *dst6,
157
};
158
159
/* Assume scopeid is valid and embed it directly */
160
if (IN6_IS_SCOPE_LINKLOCAL(dst6))
161
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
162
163
RIB_RLOCK(rh);
164
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
165
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
166
nh = nhop_select((RNTORT(rn))->rt_nhop, flowid);
167
/* Ensure route & ifp is UP */
168
if (RT_LINK_IS_UP(nh->nh_ifp)) {
169
if (flags & NHR_REF)
170
nhop_ref_object(nh);
171
RIB_RUNLOCK(rh);
172
return (nh);
173
}
174
}
175
RIB_RUNLOCK(rh);
176
177
RTSTAT_INC(rts_unreach);
178
return (NULL);
179
}
180
#endif
181
182
inline static int
183
check_urpf_nhop(const struct nhop_object *nh, uint32_t flags,
184
const struct ifnet *src_if)
185
{
186
187
if (src_if != NULL && nh->nh_aifp == src_if) {
188
return (1);
189
}
190
if (src_if == NULL) {
191
if ((flags & NHR_NODEFAULT) == 0)
192
return (1);
193
else if ((nh->nh_flags & NHF_DEFAULT) == 0)
194
return (1);
195
}
196
197
return (0);
198
}
199
200
static int
201
check_urpf(struct nhop_object *nh, uint32_t flags,
202
const struct ifnet *src_if)
203
{
204
#ifdef ROUTE_MPATH
205
if (NH_IS_NHGRP(nh)) {
206
const struct weightened_nhop *wn;
207
uint32_t num_nhops;
208
wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops);
209
for (int i = 0; i < num_nhops; i++) {
210
if (check_urpf_nhop(wn[i].nh, flags, src_if) != 0)
211
return (1);
212
}
213
return (0);
214
} else
215
#endif
216
return (check_urpf_nhop(nh, flags, src_if));
217
}
218
219
#ifndef FIB_ALGO
220
static struct nhop_object *
221
lookup_nhop(uint32_t fibnum, const struct in6_addr *dst6,
222
uint32_t scopeid)
223
{
224
RIB_RLOCK_TRACKER;
225
struct rib_head *rh;
226
struct radix_node *rn;
227
struct nhop_object *nh;
228
229
KASSERT((fibnum < rt_numfibs), ("fib6_check_urpf: bad fibnum"));
230
rh = rt_tables_get_rnh(fibnum, AF_INET6);
231
if (rh == NULL)
232
return (NULL);
233
234
/* Prepare lookup key */
235
struct sockaddr_in6 sin6 = {
236
.sin6_len = sizeof(struct sockaddr_in6),
237
.sin6_addr = *dst6,
238
};
239
240
/* Assume scopeid is valid and embed it directly */
241
if (IN6_IS_SCOPE_LINKLOCAL(dst6))
242
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
243
244
nh = NULL;
245
RIB_RLOCK(rh);
246
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
247
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0))
248
nh = RNTORT(rn)->rt_nhop;
249
RIB_RUNLOCK(rh);
250
251
return (nh);
252
}
253
#endif
254
255
/*
256
* Performs reverse path forwarding lookup.
257
* If @src_if is non-zero, verifies that at least 1 path goes via
258
* this interface.
259
* If @src_if is zero, verifies that route exist.
260
* if @flags contains NHR_NOTDEFAULT, do not consider default route.
261
*
262
* Returns 1 if route matching conditions is found, 0 otherwise.
263
*/
264
int
265
fib6_check_urpf(uint32_t fibnum, const struct in6_addr *dst6,
266
uint32_t scopeid, uint32_t flags, const struct ifnet *src_if)
267
{
268
struct nhop_object *nh;
269
#ifdef FIB_ALGO
270
struct fib_dp *dp = &V_inet6_dp[fibnum];
271
struct flm_lookup_key key = {.addr6 = dst6 };
272
273
nh = dp->f(dp->arg, key, scopeid);
274
#else
275
nh = lookup_nhop(fibnum, dst6, scopeid);
276
#endif
277
if (nh != NULL)
278
return (check_urpf(nh, flags, src_if));
279
return (0);
280
}
281
282
/*
283
* Function returning prefix match data along with the nexthop data.
284
* Intended to be used by the control plane code.
285
* Supported flags:
286
* NHR_UNLOCKED: do not lock radix during lookup.
287
* Returns pointer to rtentry and raw nexthop in @rnd. Both rtentry
288
* and nexthop are safe to use within current epoch. Note:
289
* Note: rnd_nhop can actually be the nexthop group.
290
*/
291
struct rtentry *
292
fib6_lookup_rt(uint32_t fibnum, const struct in6_addr *dst6,
293
uint32_t scopeid, uint32_t flags, struct route_nhop_data *rnd)
294
{
295
RIB_RLOCK_TRACKER;
296
struct rib_head *rh;
297
struct radix_node *rn;
298
struct rtentry *rt;
299
300
KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum"));
301
rh = rt_tables_get_rnh(fibnum, AF_INET6);
302
if (rh == NULL)
303
return (NULL);
304
305
struct sockaddr_in6 sin6 = {
306
.sin6_len = sizeof(struct sockaddr_in6),
307
.sin6_addr = *dst6,
308
};
309
310
/* Assume scopeid is valid and embed it directly */
311
if (IN6_IS_SCOPE_LINKLOCAL(dst6))
312
sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
313
314
rt = NULL;
315
if (!(flags & NHR_UNLOCKED))
316
RIB_RLOCK(rh);
317
rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
318
if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
319
rt = (struct rtentry *)rn;
320
rnd->rnd_nhop = rt->rt_nhop;
321
rnd->rnd_weight = rt->rt_weight;
322
}
323
if (!(flags & NHR_UNLOCKED))
324
RIB_RUNLOCK(rh);
325
326
return (rt);
327
}
328
329
struct nhop_object *
330
fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr *dst6,
331
uint32_t scopeid, uint32_t flags)
332
{
333
struct rtentry *rt;
334
struct route_nhop_data rnd;
335
336
rt = fib6_lookup_rt(fibnum, dst6, scopeid, NHR_UNLOCKED, &rnd);
337
if (rt != NULL) {
338
struct nhop_object *nh = nhop_select(rnd.rnd_nhop, 0);
339
/* Ensure route & ifp is UP */
340
if (RT_LINK_IS_UP(nh->nh_ifp))
341
return (nh);
342
}
343
344
return (NULL);
345
}
346
347
#endif
348
349