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 442