]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet6/in6_fib.c
netmap: vtnet: fix races in vtnet_netmap_reg()
[FreeBSD/FreeBSD.git] / sys / netinet6 / in6_fib.c
1 /*-
2  * Copyright (c) 2015
3  *      Alexander V. Chernikov <melifaro@FreeBSD.org>
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 <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_inet.h"
34 #include "opt_inet6.h"
35 #include "opt_route.h"
36 #include "opt_mpath.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/lock.h>
41 #include <sys/rmlock.h>
42 #include <sys/malloc.h>
43 #include <sys/mbuf.h>
44 #include <sys/socket.h>
45 #include <sys/sysctl.h>
46 #include <sys/kernel.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_var.h>
53 #include <net/route/nhop.h>
54 #include <net/route/shared.h>
55 #include <net/vnet.h>
56
57 #ifdef RADIX_MPATH
58 #include <net/radix_mpath.h>
59 #endif
60
61 #include <netinet/in.h>
62 #include <netinet/in_var.h>
63 #include <netinet/ip_mroute.h>
64 #include <netinet/ip6.h>
65 #include <netinet6/in6_fib.h>
66 #include <netinet6/in6_var.h>
67 #include <netinet6/nd6.h>
68 #include <netinet6/scope6_var.h>
69
70 #include <net/if_types.h>
71
72 #ifdef INET6
73 static void fib6_rte_to_nh_extended(const struct nhop_object *nh,
74     const struct in6_addr *dst, uint32_t flags, struct nhop6_extended *pnh6);
75 static void fib6_rte_to_nh_basic(const struct nhop_object *nh, const struct in6_addr *dst,
76     uint32_t flags, struct nhop6_basic *pnh6);
77
78 #define ifatoia6(ifa)   ((struct in6_ifaddr *)(ifa))
79
80 CHK_STRUCT_ROUTE_COMPAT(struct route_in6, ro_dst);
81
82
83
84 static void
85 fib6_rte_to_nh_basic(const struct nhop_object *nh, const struct in6_addr *dst,
86     uint32_t flags, struct nhop6_basic *pnh6)
87 {
88
89         /* Do explicit nexthop zero unless we're copying it */
90         memset(pnh6, 0, sizeof(*pnh6));
91
92         if ((flags & NHR_IFAIF) != 0)
93                 pnh6->nh_ifp = nh->nh_aifp;
94         else
95                 pnh6->nh_ifp = nh->nh_ifp;
96
97         pnh6->nh_mtu = nh->nh_mtu;
98         if (nh->nh_flags & NHF_GATEWAY) {
99                 /* Return address with embedded scope. */
100                 pnh6->nh_addr = nh->gw6_sa.sin6_addr;
101         } else
102                 pnh6->nh_addr = *dst;
103         /* Set flags */
104         pnh6->nh_flags = nh->nh_flags;
105 }
106
107 static void
108 fib6_rte_to_nh_extended(const struct nhop_object *nh, const struct in6_addr *dst,
109     uint32_t flags, struct nhop6_extended *pnh6)
110 {
111
112         /* Do explicit nexthop zero unless we're copying it */
113         memset(pnh6, 0, sizeof(*pnh6));
114
115         if ((flags & NHR_IFAIF) != 0)
116                 pnh6->nh_ifp = nh->nh_aifp;
117         else
118                 pnh6->nh_ifp = nh->nh_ifp;
119
120         pnh6->nh_mtu = nh->nh_mtu;
121         if (nh->nh_flags & NHF_GATEWAY) {
122                 /* Return address with embedded scope. */
123                 pnh6->nh_addr = nh->gw6_sa.sin6_addr;
124         } else
125                 pnh6->nh_addr = *dst;
126         /* Set flags */
127         pnh6->nh_flags = nh->nh_flags;
128         pnh6->nh_ia = ifatoia6(nh->nh_ifa);
129 }
130
131 /*
132  * Performs IPv6 route table lookup on @dst. Returns 0 on success.
133  * Stores basic nexthop info into provided @pnh6 structure.
134  * Note that
135  * - nh_ifp represents logical transmit interface (rt_ifp) by default
136  * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
137  * - mtu from logical transmit interface will be returned.
138  * - nh_ifp cannot be safely dereferenced
139  * - nh_ifp represents rt_ifp (e.g. if looking up address on
140  *   interface "ix0" pointer to "ix0" interface will be returned instead
141  *   of "lo0")
142  * - howewer mtu from "transmit" interface will be returned.
143  * - scope will be embedded in nh_addr
144  */
145 int
146 fib6_lookup_nh_basic(uint32_t fibnum, const struct in6_addr *dst, uint32_t scopeid,
147     uint32_t flags, uint32_t flowid, struct nhop6_basic *pnh6)
148 {
149         RIB_RLOCK_TRACKER;
150         struct rib_head *rh;
151         struct radix_node *rn;
152         struct sockaddr_in6 sin6;
153         struct nhop_object *nh;
154
155         KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum"));
156         rh = rt_tables_get_rnh(fibnum, AF_INET6);
157         if (rh == NULL)
158                 return (ENOENT);
159
160         /* Prepare lookup key */
161         memset(&sin6, 0, sizeof(sin6));
162         sin6.sin6_addr = *dst;
163         sin6.sin6_len = sizeof(struct sockaddr_in6);
164         /* Assume scopeid is valid and embed it directly */
165         if (IN6_IS_SCOPE_LINKLOCAL(dst))
166                 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
167
168         RIB_RLOCK(rh);
169         rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
170         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
171                 nh = RNTORT(rn)->rt_nhop;
172                 /* Ensure route & ifp is UP */
173                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
174                         fib6_rte_to_nh_basic(nh, &sin6.sin6_addr, flags, pnh6);
175                         RIB_RUNLOCK(rh);
176                         return (0);
177                 }
178         }
179         RIB_RUNLOCK(rh);
180
181         return (ENOENT);
182 }
183
184 /*
185  * Performs IPv6 route table lookup on @dst. Returns 0 on success.
186  * Stores extended nexthop info into provided @pnh6 structure.
187  * Note that
188  * - nh_ifp cannot be safely dereferenced unless NHR_REF is specified.
189  * - in that case you need to call fib6_free_nh_ext()
190  * - nh_ifp represents logical transmit interface (rt_ifp) by default
191  * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
192  * - mtu from logical transmit interface will be returned.
193  * - scope will be embedded in nh_addr
194  */
195 int
196 fib6_lookup_nh_ext(uint32_t fibnum, const struct in6_addr *dst,uint32_t scopeid,
197     uint32_t flags, uint32_t flowid, struct nhop6_extended *pnh6)
198 {
199         RIB_RLOCK_TRACKER;
200         struct rib_head *rh;
201         struct radix_node *rn;
202         struct sockaddr_in6 sin6;
203         struct rtentry *rte;
204         struct nhop_object *nh;
205
206         KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_ext: bad fibnum"));
207         rh = rt_tables_get_rnh(fibnum, AF_INET6);
208         if (rh == NULL)
209                 return (ENOENT);
210
211         /* Prepare lookup key */
212         memset(&sin6, 0, sizeof(sin6));
213         sin6.sin6_len = sizeof(struct sockaddr_in6);
214         sin6.sin6_addr = *dst;
215         /* Assume scopeid is valid and embed it directly */
216         if (IN6_IS_SCOPE_LINKLOCAL(dst))
217                 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
218
219         RIB_RLOCK(rh);
220         rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
221         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
222                 rte = RNTORT(rn);
223 #ifdef RADIX_MPATH
224                 rte = rt_mpath_select(rte, flowid);
225                 if (rte == NULL) {
226                         RIB_RUNLOCK(rh);
227                         return (ENOENT);
228                 }
229 #endif
230                 nh = rte->rt_nhop;
231                 /* Ensure route & ifp is UP */
232                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
233                         fib6_rte_to_nh_extended(nh, &sin6.sin6_addr, flags,
234                             pnh6);
235                         if ((flags & NHR_REF) != 0) {
236                                 /* TODO: Do lwref on egress ifp's */
237                         }
238                         RIB_RUNLOCK(rh);
239
240                         return (0);
241                 }
242         }
243         RIB_RUNLOCK(rh);
244
245         return (ENOENT);
246 }
247
248 void
249 fib6_free_nh_ext(uint32_t fibnum, struct nhop6_extended *pnh6)
250 {
251
252 }
253
254 /*
255  * Looks up path in fib @fibnum specified by @dst.
256  * Assumes scope is deembedded and provided in @scopeid.
257  *
258  * Returns path nexthop on success. Nexthop is safe to use
259  *  within the current network epoch. If longer lifetime is required,
260  *  one needs to pass NHR_REF as a flag. This will return referenced
261  *  nexthop.
262  */
263 struct nhop_object *
264 fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6,
265     uint32_t scopeid, uint32_t flags, uint32_t flowid)
266 {
267         RIB_RLOCK_TRACKER;
268         struct rib_head *rh;
269         struct radix_node *rn;
270         struct rtentry *rt;
271         struct nhop_object *nh;
272         struct sockaddr_in6 sin6;
273
274         KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum"));
275         rh = rt_tables_get_rnh(fibnum, AF_INET6);
276         if (rh == NULL)
277                 return (NULL);
278
279         /* TODO: radix changes */
280         //addr = *dst6;
281         /* Prepare lookup key */
282         memset(&sin6, 0, sizeof(sin6));
283         sin6.sin6_len = sizeof(struct sockaddr_in6);
284         sin6.sin6_addr = *dst6;
285
286         /* Assume scopeid is valid and embed it directly */
287         if (IN6_IS_SCOPE_LINKLOCAL(dst6))
288                 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
289
290         RIB_RLOCK(rh);
291         rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
292         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
293                 rt = RNTORT(rn);
294 #ifdef RADIX_MPATH
295                 if (rt_mpath_next(rt) != NULL)
296                         rt = rt_mpath_selectrte(rt, flowid);
297 #endif
298                 nh = rt->rt_nhop;
299                 /* Ensure route & ifp is UP */
300                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
301                         if (flags & NHR_REF)
302                                 nhop_ref_object(nh);
303                         RIB_RUNLOCK(rh);
304                         return (nh);
305                 }
306         }
307         RIB_RUNLOCK(rh);
308
309         RTSTAT_INC(rts_unreach);
310         return (NULL);
311 }
312
313 inline static int
314 check_urpf(const struct nhop_object *nh, uint32_t flags,
315     const struct ifnet *src_if)
316 {
317
318         if (src_if != NULL && nh->nh_aifp == src_if) {
319                 return (1);
320         }
321         if (src_if == NULL) {
322                 if ((flags & NHR_NODEFAULT) == 0)
323                         return (1);
324                 else if ((nh->nh_flags & NHF_DEFAULT) == 0)
325                         return (1);
326         }
327
328         return (0);
329 }
330
331 #ifdef RADIX_MPATH
332 inline static int
333 check_urpf_mpath(struct rtentry *rt, uint32_t flags,
334     const struct ifnet *src_if)
335 {
336         
337         while (rt != NULL) {
338                 if (check_urpf(rt->rt_nhop, flags, src_if) != 0)
339                         return (1);
340                 rt = rt_mpath_next(rt);
341         }
342
343         return (0);
344 }
345 #endif
346
347 /*
348  * Performs reverse path forwarding lookup.
349  * If @src_if is non-zero, verifies that at least 1 path goes via
350  *   this interface.
351  * If @src_if is zero, verifies that route exist.
352  * if @flags contains NHR_NOTDEFAULT, do not consider default route.
353  *
354  * Returns 1 if route matching conditions is found, 0 otherwise.
355  */
356 int
357 fib6_check_urpf(uint32_t fibnum, const struct in6_addr *dst6,
358     uint32_t scopeid, uint32_t flags, const struct ifnet *src_if)
359 {
360         RIB_RLOCK_TRACKER;
361         struct rib_head *rh;
362         struct radix_node *rn;
363         struct rtentry *rt;
364         struct sockaddr_in6 sin6;
365         int ret;
366
367         KASSERT((fibnum < rt_numfibs), ("fib6_check_urpf: bad fibnum"));
368         rh = rt_tables_get_rnh(fibnum, AF_INET6);
369         if (rh == NULL)
370                 return (0);
371
372         /* TODO: radix changes */
373         /* Prepare lookup key */
374         memset(&sin6, 0, sizeof(sin6));
375         sin6.sin6_len = sizeof(struct sockaddr_in6);
376         sin6.sin6_addr = *dst6;
377
378         /* Assume scopeid is valid and embed it directly */
379         if (IN6_IS_SCOPE_LINKLOCAL(dst6))
380                 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
381
382         RIB_RLOCK(rh);
383         rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
384         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
385                 rt = RNTORT(rn);
386 #ifdef  RADIX_MPATH
387                 ret = check_urpf_mpath(rt, flags, src_if);
388 #else
389                 ret = check_urpf(rt->rt_nhop, flags, src_if);
390 #endif
391                 RIB_RUNLOCK(rh);
392                 return (ret);
393         }
394         RIB_RUNLOCK(rh);
395
396         return (0);
397 }
398
399 struct nhop_object *
400 fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr *dst6,
401     uint32_t scopeid, uint32_t flags)
402 {
403         struct rib_head *rh;
404         struct radix_node *rn;
405         struct rtentry *rt;
406         struct nhop_object *nh;
407         struct sockaddr_in6 sin6;
408
409         KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum"));
410         rh = rt_tables_get_rnh(fibnum, AF_INET6);
411         if (rh == NULL)
412                 return (NULL);
413
414         /* TODO: radix changes */
415         //addr = *dst6;
416         /* Prepare lookup key */
417         memset(&sin6, 0, sizeof(sin6));
418         sin6.sin6_len = sizeof(struct sockaddr_in6);
419         sin6.sin6_addr = *dst6;
420
421         /* Assume scopeid is valid and embed it directly */
422         if (IN6_IS_SCOPE_LINKLOCAL(dst6))
423                 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
424
425         rn = rh->rnh_matchaddr((void *)&sin6, &rh->head);
426         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
427                 rt = RNTORT(rn);
428                 nh = rt->rt_nhop;
429                 /* Ensure route & ifp is UP */
430                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
431                         if (flags & NHR_REF)
432                                 nhop_ref_object(nh);
433                         return (nh);
434                 }
435         }
436
437         return (NULL);
438 }
439
440 #endif
441