]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/in_fib.c
Convert debugnet to the new routing KPI.
[FreeBSD/FreeBSD.git] / sys / netinet / in_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_route.h"
35 #include "opt_mpath.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/lock.h>
40 #include <sys/rmlock.h>
41 #include <sys/malloc.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <sys/sysctl.h>
45 #include <sys/kernel.h>
46
47 #include <net/if.h>
48 #include <net/if_var.h>
49 #include <net/if_dl.h>
50 #include <net/route.h>
51 #include <net/route_var.h>
52 #include <net/route/nhop.h>
53 #include <net/route/shared.h>
54 #include <net/vnet.h>
55
56 #ifdef RADIX_MPATH
57 #include <net/radix_mpath.h>
58 #endif
59
60 #include <netinet/in.h>
61 #include <netinet/in_var.h>
62 #include <netinet/in_fib.h>
63
64 #ifdef INET
65
66 /* Verify struct route compatiblity */
67 /* Assert 'struct route_in' is compatible with 'struct route' */
68 CHK_STRUCT_ROUTE_COMPAT(struct route_in, ro_dst4);
69 static void fib4_rte_to_nh_basic(struct nhop_object *nh, struct in_addr dst,
70     uint32_t flags, struct nhop4_basic *pnh4);
71 static void fib4_rte_to_nh_extended(struct nhop_object *nh, struct in_addr dst,
72     uint32_t flags, struct nhop4_extended *pnh4);
73
74 #define RNTORT(p)       ((struct rtentry *)(p))
75
76 static void
77 fib4_rte_to_nh_basic(struct nhop_object *nh, struct in_addr dst,
78     uint32_t flags, struct nhop4_basic *pnh4)
79 {
80
81         if ((flags & NHR_IFAIF) != 0)
82                 pnh4->nh_ifp = nh->nh_ifa->ifa_ifp;
83         else
84                 pnh4->nh_ifp = nh->nh_ifp;
85         pnh4->nh_mtu = nh->nh_mtu;
86         if (nh->nh_flags & NHF_GATEWAY)
87                 pnh4->nh_addr = nh->gw4_sa.sin_addr;
88         else
89                 pnh4->nh_addr = dst;
90         /* Set flags */
91         pnh4->nh_flags = nh->nh_flags;
92         /* TODO: Handle RTF_BROADCAST here */
93 }
94
95 static void
96 fib4_rte_to_nh_extended(struct nhop_object *nh, struct in_addr dst,
97     uint32_t flags, struct nhop4_extended *pnh4)
98 {
99
100         if ((flags & NHR_IFAIF) != 0)
101                 pnh4->nh_ifp = nh->nh_ifa->ifa_ifp;
102         else
103                 pnh4->nh_ifp = nh->nh_ifp;
104         pnh4->nh_mtu = nh->nh_mtu;
105         if (nh->nh_flags & NHF_GATEWAY)
106                 pnh4->nh_addr = nh->gw4_sa.sin_addr;
107         else
108                 pnh4->nh_addr = dst;
109         /* Set flags */
110         pnh4->nh_flags = nh->nh_flags;
111         pnh4->nh_ia = ifatoia(nh->nh_ifa);
112         pnh4->nh_src = IA_SIN(pnh4->nh_ia)->sin_addr;
113 }
114
115 /*
116  * Performs IPv4 route table lookup on @dst. Returns 0 on success.
117  * Stores nexthop info provided @pnh4 structure.
118  * Note that
119  * - nh_ifp cannot be safely dereferenced
120  * - nh_ifp represents logical transmit interface (rt_ifp) (e.g. if
121  *   looking up address on interface "ix0" pointer to "lo0" interface
122  *   will be returned instead of "ix0")
123  * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
124  * - howewer mtu from "transmit" interface will be returned.
125  */
126 int
127 fib4_lookup_nh_basic(uint32_t fibnum, struct in_addr dst, uint32_t flags,
128     uint32_t flowid, struct nhop4_basic *pnh4)
129 {
130         RIB_RLOCK_TRACKER;
131         struct rib_head *rh;
132         struct radix_node *rn;
133         struct sockaddr_in sin;
134         struct nhop_object *nh;
135
136         KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_basic: bad fibnum"));
137         rh = rt_tables_get_rnh(fibnum, AF_INET);
138         if (rh == NULL)
139                 return (ENOENT);
140
141         /* Prepare lookup key */
142         memset(&sin, 0, sizeof(sin));
143         sin.sin_len = sizeof(struct sockaddr_in);
144         sin.sin_addr = dst;
145
146         RIB_RLOCK(rh);
147         rn = rh->rnh_matchaddr((void *)&sin, &rh->head);
148         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
149                 nh = RNTORT(rn)->rt_nhop;
150                 /* Ensure route & ifp is UP */
151                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
152                         fib4_rte_to_nh_basic(nh, dst, flags, pnh4);
153                         RIB_RUNLOCK(rh);
154
155                         return (0);
156                 }
157         }
158         RIB_RUNLOCK(rh);
159
160         return (ENOENT);
161 }
162
163 /*
164  * Performs IPv4 route table lookup on @dst. Returns 0 on success.
165  * Stores extende nexthop info provided @pnh4 structure.
166  * Note that
167  * - nh_ifp cannot be safely dereferenced unless NHR_REF is specified.
168  * - in that case you need to call fib4_free_nh_ext()
169  * - nh_ifp represents logical transmit interface (rt_ifp) (e.g. if
170  *   looking up address of interface "ix0" pointer to "lo0" interface
171  *   will be returned instead of "ix0")
172  * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
173  * - howewer mtu from "transmit" interface will be returned.
174  */
175 int
176 fib4_lookup_nh_ext(uint32_t fibnum, struct in_addr dst, uint32_t flags,
177     uint32_t flowid, struct nhop4_extended *pnh4)
178 {
179         RIB_RLOCK_TRACKER;
180         struct rib_head *rh;
181         struct radix_node *rn;
182         struct sockaddr_in sin;
183         struct rtentry *rte;
184         struct nhop_object *nh;
185
186         KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_ext: bad fibnum"));
187         rh = rt_tables_get_rnh(fibnum, AF_INET);
188         if (rh == NULL)
189                 return (ENOENT);
190
191         /* Prepare lookup key */
192         memset(&sin, 0, sizeof(sin));
193         sin.sin_len = sizeof(struct sockaddr_in);
194         sin.sin_addr = dst;
195
196         RIB_RLOCK(rh);
197         rn = rh->rnh_matchaddr((void *)&sin, &rh->head);
198         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
199                 rte = RNTORT(rn);
200 #ifdef RADIX_MPATH
201                 rte = rt_mpath_select(rte, flowid);
202                 if (rte == NULL) {
203                         RIB_RUNLOCK(rh);
204                         return (ENOENT);
205                 }
206 #endif
207                 nh = rte->rt_nhop;
208                 /* Ensure route & ifp is UP */
209                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
210                         fib4_rte_to_nh_extended(nh, dst, flags, pnh4);
211                         if ((flags & NHR_REF) != 0) {
212                                 /* TODO: lwref on egress ifp's ? */
213                         }
214                         RIB_RUNLOCK(rh);
215
216                         return (0);
217                 }
218         }
219         RIB_RUNLOCK(rh);
220
221         return (ENOENT);
222 }
223
224 void
225 fib4_free_nh_ext(uint32_t fibnum, struct nhop4_extended *pnh4)
226 {
227
228 }
229
230 /*
231  * Looks up path in fib @fibnum specified by @dst.
232  * Returns path nexthop on success. Nexthop is safe to use
233  *  within the current network epoch. If longer lifetime is required,
234  *  one needs to pass NHR_REF as a flag. This will return referenced
235  *  nexthop.
236  */
237 struct nhop_object *
238 fib4_lookup(uint32_t fibnum, struct in_addr dst, uint32_t scopeid,
239     uint32_t flags, uint32_t flowid)
240 {
241         RIB_RLOCK_TRACKER;
242         struct rib_head *rh;
243         struct radix_node *rn;
244         struct rtentry *rt;
245         struct nhop_object *nh;
246
247         KASSERT((fibnum < rt_numfibs), ("fib4_lookup: bad fibnum"));
248         rh = rt_tables_get_rnh(fibnum, AF_INET);
249         if (rh == NULL)
250                 return (NULL);
251
252         /* Prepare lookup key */
253         struct sockaddr_in sin4;
254         memset(&sin4, 0, sizeof(sin4));
255         sin4.sin_family = AF_INET;
256         sin4.sin_len = sizeof(struct sockaddr_in);
257         sin4.sin_addr = dst;
258
259         nh = NULL;
260         RIB_RLOCK(rh);
261         rn = rh->rnh_matchaddr((void *)&sin4, &rh->head);
262         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
263                 rt = RNTORT(rn);
264 #ifdef RADIX_MPATH
265                 if (rt_mpath_next(rt) != NULL)
266                         rt = rt_mpath_selectrte(rt, flowid);
267 #endif
268                 nh = rt->rt_nhop;
269                 /* Ensure route & ifp is UP */
270                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
271                         if (flags & NHR_REF)
272                                 nhop_ref_object(nh);
273                         RIB_RUNLOCK(rh);
274                         return (nh);
275                 }
276         }
277         RIB_RUNLOCK(rh);
278
279         RTSTAT_INC(rts_unreach);
280         return (NULL);
281 }
282
283 inline static int
284 check_urpf(const struct nhop_object *nh, uint32_t flags,
285     const struct ifnet *src_if)
286 {
287
288         if (src_if != NULL && nh->nh_aifp == src_if) {
289                 return (1);
290         }
291         if (src_if == NULL) {
292                 if ((flags & NHR_NODEFAULT) == 0)
293                         return (1);
294                 else if ((nh->nh_flags & NHF_DEFAULT) == 0)
295                         return (1);
296         }
297
298         return (0);
299 }
300
301 #ifdef RADIX_MPATH
302 inline static int
303 check_urpf_mpath(struct rtentry *rt, uint32_t flags,
304     const struct ifnet *src_if)
305 {
306         
307         while (rt != NULL) {
308                 if (check_urpf(rt->rt_nhop, flags, src_if) != 0)
309                         return (1);
310                 rt = rt_mpath_next(rt);
311         }
312
313         return (0);
314 }
315 #endif
316
317 /*
318  * Performs reverse path forwarding lookup.
319  * If @src_if is non-zero, verifies that at least 1 path goes via
320  *   this interface.
321  * If @src_if is zero, verifies that route exist.
322  * if @flags contains NHR_NOTDEFAULT, do not consider default route.
323  *
324  * Returns 1 if route matching conditions is found, 0 otherwise.
325  */
326 int
327 fib4_check_urpf(uint32_t fibnum, struct in_addr dst, uint32_t scopeid,
328   uint32_t flags, const struct ifnet *src_if)
329 {
330         RIB_RLOCK_TRACKER;
331         struct rib_head *rh;
332         struct radix_node *rn;
333         struct rtentry *rt;
334         int ret;
335
336         KASSERT((fibnum < rt_numfibs), ("fib4_check_urpf: bad fibnum"));
337         rh = rt_tables_get_rnh(fibnum, AF_INET);
338         if (rh == NULL)
339                 return (0);
340
341         /* Prepare lookup key */
342         struct sockaddr_in sin4;
343         memset(&sin4, 0, sizeof(sin4));
344         sin4.sin_len = sizeof(struct sockaddr_in);
345         sin4.sin_addr = dst;
346
347         RIB_RLOCK(rh);
348         rn = rh->rnh_matchaddr((void *)&sin4, &rh->head);
349         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
350                 rt = RNTORT(rn);
351 #ifdef  RADIX_MPATH
352                 ret = check_urpf_mpath(rt, flags, src_if);
353 #else
354                 ret = check_urpf(rt->rt_nhop, flags, src_if);
355 #endif
356                 RIB_RUNLOCK(rh);
357                 return (ret);
358         }
359         RIB_RUNLOCK(rh);
360
361         return (0);
362 }
363
364 struct nhop_object *
365 fib4_lookup_debugnet(uint32_t fibnum, struct in_addr dst, uint32_t scopeid,
366     uint32_t flags)
367 {
368         struct rib_head *rh;
369         struct radix_node *rn;
370         struct rtentry *rt;
371         struct nhop_object *nh;
372
373         KASSERT((fibnum < rt_numfibs), ("fib4_lookup_debugnet: bad fibnum"));
374         rh = rt_tables_get_rnh(fibnum, AF_INET);
375         if (rh == NULL)
376                 return (NULL);
377
378         /* Prepare lookup key */
379         struct sockaddr_in sin4;
380         memset(&sin4, 0, sizeof(sin4));
381         sin4.sin_family = AF_INET;
382         sin4.sin_len = sizeof(struct sockaddr_in);
383         sin4.sin_addr = dst;
384
385         nh = NULL;
386         /* unlocked lookup */
387         rn = rh->rnh_matchaddr((void *)&sin4, &rh->head);
388         if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
389                 rt = RNTORT(rn);
390 #ifdef RADIX_MPATH
391                 if (rt_mpath_next(rt) != NULL)
392                         rt = rt_mpath_selectrte(rt, 0);
393 #endif
394                 nh = rt->rt_nhop;
395                 /* Ensure route & ifp is UP */
396                 if (RT_LINK_IS_UP(nh->nh_ifp)) {
397                         if (flags & NHR_REF)
398                                 nhop_ref_object(nh);
399                         return (nh);
400                 }
401         }
402
403         return (NULL);
404 }
405
406 #endif