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 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/route_ctl.h> 52 #include <net/route/route_var.h> 53 #include <net/route/nhop.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 /* 72 * Looks up path in fib @fibnum specified by @dst. 73 * Assumes scope is deembedded and provided in @scopeid. 74 * 75 * Returns path nexthop on success. Nexthop is safe to use 76 * within the current network epoch. If longer lifetime is required, 77 * one needs to pass NHR_REF as a flag. This will return referenced 78 * nexthop. 79 */ 80 struct nhop_object * 81 fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6, 82 uint32_t scopeid, uint32_t flags, uint32_t flowid) 83 { 84 RIB_RLOCK_TRACKER; 85 struct rib_head *rh; 86 struct radix_node *rn; 87 struct nhop_object *nh; 88 struct sockaddr_in6 sin6; 89 90 KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); 91 rh = rt_tables_get_rnh(fibnum, AF_INET6); 92 if (rh == NULL) 93 return (NULL); 94 95 /* TODO: radix changes */ 96 //addr = *dst6; 97 /* Prepare lookup key */ 98 memset(&sin6, 0, sizeof(sin6)); 99 sin6.sin6_len = sizeof(struct sockaddr_in6); 100 sin6.sin6_addr = *dst6; 101 102 /* Assume scopeid is valid and embed it directly */ 103 if (IN6_IS_SCOPE_LINKLOCAL(dst6)) 104 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); 105 106 RIB_RLOCK(rh); 107 rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); 108 if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { 109 nh = nhop_select((RNTORT(rn))->rt_nhop, flowid); 110 /* Ensure route & ifp is UP */ 111 if (RT_LINK_IS_UP(nh->nh_ifp)) { 112 if (flags & NHR_REF) 113 nhop_ref_object(nh); 114 RIB_RUNLOCK(rh); 115 return (nh); 116 } 117 } 118 RIB_RUNLOCK(rh); 119 120 RTSTAT_INC(rts_unreach); 121 return (NULL); 122 } 123 124 inline static int 125 check_urpf_nhop(const struct nhop_object *nh, uint32_t flags, 126 const struct ifnet *src_if) 127 { 128 129 if (src_if != NULL && nh->nh_aifp == src_if) { 130 return (1); 131 } 132 if (src_if == NULL) { 133 if ((flags & NHR_NODEFAULT) == 0) 134 return (1); 135 else if ((nh->nh_flags & NHF_DEFAULT) == 0) 136 return (1); 137 } 138 139 return (0); 140 } 141 142 static int 143 check_urpf(struct nhop_object *nh, uint32_t flags, 144 const struct ifnet *src_if) 145 { 146 #ifdef ROUTE_MPATH 147 if (NH_IS_NHGRP(nh)) { 148 struct weightened_nhop *wn; 149 uint32_t num_nhops; 150 wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops); 151 for (int i = 0; i < num_nhops; i++) { 152 if (check_urpf_nhop(wn[i].nh, flags, src_if) != 0) 153 return (1); 154 } 155 return (0); 156 } else 157 #endif 158 return (check_urpf_nhop(nh, flags, src_if)); 159 } 160 161 /* 162 * Performs reverse path forwarding lookup. 163 * If @src_if is non-zero, verifies that at least 1 path goes via 164 * this interface. 165 * If @src_if is zero, verifies that route exist. 166 * if @flags contains NHR_NOTDEFAULT, do not consider default route. 167 * 168 * Returns 1 if route matching conditions is found, 0 otherwise. 169 */ 170 int 171 fib6_check_urpf(uint32_t fibnum, const struct in6_addr *dst6, 172 uint32_t scopeid, uint32_t flags, const struct ifnet *src_if) 173 { 174 RIB_RLOCK_TRACKER; 175 struct rib_head *rh; 176 struct radix_node *rn; 177 struct sockaddr_in6 sin6; 178 int ret; 179 180 KASSERT((fibnum < rt_numfibs), ("fib6_check_urpf: bad fibnum")); 181 rh = rt_tables_get_rnh(fibnum, AF_INET6); 182 if (rh == NULL) 183 return (0); 184 185 /* TODO: radix changes */ 186 /* Prepare lookup key */ 187 memset(&sin6, 0, sizeof(sin6)); 188 sin6.sin6_len = sizeof(struct sockaddr_in6); 189 sin6.sin6_addr = *dst6; 190 191 /* Assume scopeid is valid and embed it directly */ 192 if (IN6_IS_SCOPE_LINKLOCAL(dst6)) 193 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); 194 195 RIB_RLOCK(rh); 196 rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); 197 if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { 198 ret = check_urpf(RNTORT(rn)->rt_nhop, flags, src_if); 199 RIB_RUNLOCK(rh); 200 return (ret); 201 } 202 RIB_RUNLOCK(rh); 203 204 return (0); 205 } 206 207 struct nhop_object * 208 fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr *dst6, 209 uint32_t scopeid, uint32_t flags) 210 { 211 struct rib_head *rh; 212 struct radix_node *rn; 213 struct nhop_object *nh; 214 struct sockaddr_in6 sin6; 215 216 KASSERT((fibnum < rt_numfibs), ("fib6_lookup: bad fibnum")); 217 rh = rt_tables_get_rnh(fibnum, AF_INET6); 218 if (rh == NULL) 219 return (NULL); 220 221 /* TODO: radix changes */ 222 //addr = *dst6; 223 /* Prepare lookup key */ 224 memset(&sin6, 0, sizeof(sin6)); 225 sin6.sin6_len = sizeof(struct sockaddr_in6); 226 sin6.sin6_addr = *dst6; 227 228 /* Assume scopeid is valid and embed it directly */ 229 if (IN6_IS_SCOPE_LINKLOCAL(dst6)) 230 sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); 231 232 rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); 233 if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { 234 nh = nhop_select((RNTORT(rn))->rt_nhop, 0); 235 /* Ensure route & ifp is UP */ 236 if (RT_LINK_IS_UP(nh->nh_ifp)) { 237 if (flags & NHR_REF) 238 nhop_ref_object(nh); 239 return (nh); 240 } 241 } 242 243 return (NULL); 244 } 245 246 #endif 247