xref: /f-stack/lib/ff_route.c (revision 2317ada5)
1144c6bcdSlogwang /*
2144c6bcdSlogwang  * Copyright (c) 1988, 1991, 1993
3144c6bcdSlogwang  *  The Regents of the University of California.  All rights reserved.
4*2317ada5Sfengbojiang  * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company.
5144c6bcdSlogwang  * All rights reserved.
6144c6bcdSlogwang  *
7144c6bcdSlogwang  * Redistribution and use in source and binary forms, with or without
8144c6bcdSlogwang  * modification, are permitted provided that the following conditions
9144c6bcdSlogwang  * are met:
10144c6bcdSlogwang  * 1. Redistributions of source code must retain the above copyright
11144c6bcdSlogwang  *    notice, this list of conditions and the following disclaimer.
12144c6bcdSlogwang  * 2. Redistributions in binary form must reproduce the above copyright
13144c6bcdSlogwang  *    notice, this list of conditions and the following disclaimer in the
14144c6bcdSlogwang  *    documentation and/or other materials provided with the distribution.
15144c6bcdSlogwang  * 4. Neither the name of the University nor the names of its contributors
16144c6bcdSlogwang  *    may be used to endorse or promote products derived from this software
17144c6bcdSlogwang  *    without specific prior written permission.
18144c6bcdSlogwang  *
19144c6bcdSlogwang  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20144c6bcdSlogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21144c6bcdSlogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22144c6bcdSlogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23144c6bcdSlogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24144c6bcdSlogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25144c6bcdSlogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26144c6bcdSlogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27144c6bcdSlogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28144c6bcdSlogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29144c6bcdSlogwang  * SUCH DAMAGE.
30144c6bcdSlogwang  *
31144c6bcdSlogwang  * Copied part from FreeBSD rtsock.c.
32144c6bcdSlogwang  *
33144c6bcdSlogwang  */
34144c6bcdSlogwang 
35144c6bcdSlogwang #include <sys/param.h>
36144c6bcdSlogwang #include <sys/proc.h>
37144c6bcdSlogwang #include <sys/jail.h>
38144c6bcdSlogwang #include <sys/kernel.h>
39144c6bcdSlogwang #include <sys/domain.h>
40144c6bcdSlogwang #include <sys/lock.h>
41144c6bcdSlogwang #include <sys/malloc.h>
42144c6bcdSlogwang #include <sys/socket.h>
43144c6bcdSlogwang #include <sys/socketvar.h>
4422ce4affSfengbojiang #include <sys/priv.h>
4522ce4affSfengbojiang #include <sys/rmlock.h>
46144c6bcdSlogwang 
47144c6bcdSlogwang #include <net/if.h>
48144c6bcdSlogwang #include <net/if_var.h>
49144c6bcdSlogwang #include <net/if_dl.h>
50144c6bcdSlogwang #include <net/if_llatbl.h>
51144c6bcdSlogwang #include <net/if_types.h>
52144c6bcdSlogwang #include <net/route.h>
5322ce4affSfengbojiang #include <net/route/route_var.h>
5422ce4affSfengbojiang #include <net/route/route_ctl.h>
5522ce4affSfengbojiang #include <net/route/nhgrp_var.h>
56144c6bcdSlogwang #include <netinet/if_ether.h>
57d7140ab7Sfengbojiang(姜凤波) #ifdef INET6
58d7140ab7Sfengbojiang(姜凤波) #include <netinet6/scope6_var.h>
59d7140ab7Sfengbojiang(姜凤波) #include <netinet6/ip6_var.h>
6022ce4affSfengbojiang #include <netinet6/in6_var.h>
61d7140ab7Sfengbojiang(姜凤波) #endif
62144c6bcdSlogwang 
63144c6bcdSlogwang #include "ff_api.h"
64144c6bcdSlogwang #include "ff_host_interface.h"
65144c6bcdSlogwang 
66144c6bcdSlogwang #ifndef _SOCKADDR_UNION_DEFINED
67144c6bcdSlogwang #define _SOCKADDR_UNION_DEFINED
68144c6bcdSlogwang /*
69144c6bcdSlogwang  * The union of all possible address formats we handle.
70144c6bcdSlogwang  */
71144c6bcdSlogwang union sockaddr_union {
72144c6bcdSlogwang     struct sockaddr     sa;
73144c6bcdSlogwang     struct sockaddr_in  sin;
74144c6bcdSlogwang     struct sockaddr_in6 sin6;
75144c6bcdSlogwang };
76144c6bcdSlogwang #endif /* _SOCKADDR_UNION_DEFINED */
77144c6bcdSlogwang 
78144c6bcdSlogwang static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, };
79144c6bcdSlogwang 
80144c6bcdSlogwang struct walkarg {
81144c6bcdSlogwang     int w_tmemsize;
82144c6bcdSlogwang     int w_op, w_arg;
83144c6bcdSlogwang     caddr_t w_tmem;
84144c6bcdSlogwang     struct sysctl_req *w_req;
85144c6bcdSlogwang };
86144c6bcdSlogwang 
87144c6bcdSlogwang /*
88144c6bcdSlogwang  * Extract the addresses of the passed sockaddrs.
89144c6bcdSlogwang  * Do a little sanity checking so as to avoid bad memory references.
90144c6bcdSlogwang  * This data is derived straight from userland.
91144c6bcdSlogwang  */
92144c6bcdSlogwang static int
rt_xaddrs(caddr_t cp,caddr_t cplim,struct rt_addrinfo * rtinfo)93144c6bcdSlogwang rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
94144c6bcdSlogwang {
95144c6bcdSlogwang     struct sockaddr *sa;
96144c6bcdSlogwang     int i;
97144c6bcdSlogwang 
98144c6bcdSlogwang     for (i = 0; i < RTAX_MAX && cp < cplim; i++) {
99144c6bcdSlogwang         if ((rtinfo->rti_addrs & (1 << i)) == 0)
100144c6bcdSlogwang             continue;
101144c6bcdSlogwang         sa = (struct sockaddr *)cp;
102144c6bcdSlogwang         /*
103144c6bcdSlogwang          * It won't fit.
104144c6bcdSlogwang          */
105144c6bcdSlogwang         if (cp + sa->sa_len > cplim)
106144c6bcdSlogwang             return (EINVAL);
107144c6bcdSlogwang         /*
108144c6bcdSlogwang          * there are no more.. quit now
109144c6bcdSlogwang          * If there are more bits, they are in error.
110144c6bcdSlogwang          * I've seen this. route(1) can evidently generate these.
111144c6bcdSlogwang          * This causes kernel to core dump.
112144c6bcdSlogwang          * for compatibility, If we see this, point to a safe address.
113144c6bcdSlogwang          */
114144c6bcdSlogwang         if (sa->sa_len == 0) {
115144c6bcdSlogwang             rtinfo->rti_info[i] = &sa_zero;
116144c6bcdSlogwang             return (0); /* should be EINVAL but for compat */
117144c6bcdSlogwang         }
118144c6bcdSlogwang         /* accept it */
119144c6bcdSlogwang #ifdef INET6
120144c6bcdSlogwang         if (sa->sa_family == AF_INET6)
121144c6bcdSlogwang             sa6_embedscope((struct sockaddr_in6 *)sa,
122144c6bcdSlogwang                 V_ip6_use_defzone);
123144c6bcdSlogwang #endif
124144c6bcdSlogwang         rtinfo->rti_info[i] = sa;
125144c6bcdSlogwang         cp += SA_SIZE(sa);
126144c6bcdSlogwang     }
127144c6bcdSlogwang     return (0);
128144c6bcdSlogwang }
129144c6bcdSlogwang 
13022ce4affSfengbojiang static inline void
fill_sockaddr_inet(struct sockaddr_in * sin,struct in_addr addr)13122ce4affSfengbojiang fill_sockaddr_inet(struct sockaddr_in *sin, struct in_addr addr)
13222ce4affSfengbojiang {
13322ce4affSfengbojiang     const struct sockaddr_in nsin = {
13422ce4affSfengbojiang         .sin_family = AF_INET,
13522ce4affSfengbojiang         .sin_len = sizeof(struct sockaddr_in),
13622ce4affSfengbojiang         .sin_addr = addr,
13722ce4affSfengbojiang     };
13822ce4affSfengbojiang     *sin = nsin;
13922ce4affSfengbojiang }
14022ce4affSfengbojiang 
14122ce4affSfengbojiang #ifdef INET6
14222ce4affSfengbojiang static inline void
fill_sockaddr_inet6(struct sockaddr_in6 * sin6,const struct in6_addr * addr6,uint32_t scopeid)14322ce4affSfengbojiang fill_sockaddr_inet6(struct sockaddr_in6 *sin6, const struct in6_addr *addr6,
14422ce4affSfengbojiang     uint32_t scopeid)
14522ce4affSfengbojiang {
14622ce4affSfengbojiang 
14722ce4affSfengbojiang     const struct sockaddr_in6 nsin6 = {
14822ce4affSfengbojiang         .sin6_family = AF_INET6,
14922ce4affSfengbojiang         .sin6_len = sizeof(struct sockaddr_in6),
15022ce4affSfengbojiang         .sin6_addr = *addr6,
15122ce4affSfengbojiang         .sin6_scope_id = scopeid,
15222ce4affSfengbojiang     };
15322ce4affSfengbojiang     *sin6 = nsin6;
15422ce4affSfengbojiang }
15522ce4affSfengbojiang #endif
15622ce4affSfengbojiang 
15722ce4affSfengbojiang /*
15822ce4affSfengbojiang  * Checks if gateway is suitable for lltable operations.
15922ce4affSfengbojiang  * Lltable code requires AF_LINK gateway with ifindex
16022ce4affSfengbojiang  *  and mac address specified.
16122ce4affSfengbojiang  * Returns 0 on success.
16222ce4affSfengbojiang  */
16322ce4affSfengbojiang static int
cleanup_xaddrs_lladdr(struct rt_addrinfo * info)16422ce4affSfengbojiang cleanup_xaddrs_lladdr(struct rt_addrinfo *info)
16522ce4affSfengbojiang {
16622ce4affSfengbojiang     struct sockaddr_dl *sdl = (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY];
16722ce4affSfengbojiang 
16822ce4affSfengbojiang     if (sdl->sdl_family != AF_LINK)
16922ce4affSfengbojiang         return (EINVAL);
17022ce4affSfengbojiang 
17122ce4affSfengbojiang     if (sdl->sdl_index == 0)
17222ce4affSfengbojiang         return (EINVAL);
17322ce4affSfengbojiang 
17422ce4affSfengbojiang     if (offsetof(struct sockaddr_dl, sdl_data) + sdl->sdl_nlen + sdl->sdl_alen > sdl->sdl_len)
17522ce4affSfengbojiang         return (EINVAL);
17622ce4affSfengbojiang 
17722ce4affSfengbojiang     return (0);
17822ce4affSfengbojiang }
17922ce4affSfengbojiang 
18022ce4affSfengbojiang static int
cleanup_xaddrs_gateway(struct rt_addrinfo * info)18122ce4affSfengbojiang cleanup_xaddrs_gateway(struct rt_addrinfo *info)
18222ce4affSfengbojiang {
18322ce4affSfengbojiang     struct sockaddr *gw = info->rti_info[RTAX_GATEWAY];
18422ce4affSfengbojiang 
18522ce4affSfengbojiang     if (info->rti_flags & RTF_LLDATA)
18622ce4affSfengbojiang         return (cleanup_xaddrs_lladdr(info));
18722ce4affSfengbojiang 
18822ce4affSfengbojiang     switch (gw->sa_family) {
18922ce4affSfengbojiang     case AF_INET:
19022ce4affSfengbojiang         {
19122ce4affSfengbojiang             struct sockaddr_in *gw_sin = (struct sockaddr_in *)gw;
19222ce4affSfengbojiang             if (gw_sin->sin_len < sizeof(struct sockaddr_in)) {
19322ce4affSfengbojiang                 printf("gw sin_len too small\n");
19422ce4affSfengbojiang                 return (EINVAL);
19522ce4affSfengbojiang             }
19622ce4affSfengbojiang             fill_sockaddr_inet(gw_sin, gw_sin->sin_addr);
19722ce4affSfengbojiang         }
19822ce4affSfengbojiang         break;
19922ce4affSfengbojiang #ifdef INET6
20022ce4affSfengbojiang     case AF_INET6:
20122ce4affSfengbojiang         {
20222ce4affSfengbojiang             struct sockaddr_in6 *gw_sin6 = (struct sockaddr_in6 *)gw;
20322ce4affSfengbojiang             if (gw_sin6->sin6_len < sizeof(struct sockaddr_in6)) {
20422ce4affSfengbojiang                 printf("gw sin6_len too small\n");
20522ce4affSfengbojiang                 return (EINVAL);
20622ce4affSfengbojiang             }
20722ce4affSfengbojiang             fill_sockaddr_inet6(gw_sin6, &gw_sin6->sin6_addr, 0);
20822ce4affSfengbojiang             break;
20922ce4affSfengbojiang         }
21022ce4affSfengbojiang #endif
21122ce4affSfengbojiang     case AF_LINK:
21222ce4affSfengbojiang         {
21322ce4affSfengbojiang             struct sockaddr_dl_short *gw_sdl;
21422ce4affSfengbojiang 
21522ce4affSfengbojiang             gw_sdl = (struct sockaddr_dl_short *)gw;
21622ce4affSfengbojiang             if (gw_sdl->sdl_len < sizeof(struct sockaddr_dl_short)) {
21722ce4affSfengbojiang                 printf("gw sdl_len too small\n");
21822ce4affSfengbojiang                 return (EINVAL);
21922ce4affSfengbojiang             }
22022ce4affSfengbojiang 
22122ce4affSfengbojiang             const struct sockaddr_dl_short sdl = {
22222ce4affSfengbojiang                 .sdl_family = AF_LINK,
22322ce4affSfengbojiang                 .sdl_len = sizeof(struct sockaddr_dl_short),
22422ce4affSfengbojiang                 .sdl_index = gw_sdl->sdl_index,
22522ce4affSfengbojiang             };
22622ce4affSfengbojiang             *gw_sdl = sdl;
22722ce4affSfengbojiang             break;
22822ce4affSfengbojiang         }
22922ce4affSfengbojiang     }
23022ce4affSfengbojiang 
23122ce4affSfengbojiang     return (0);
23222ce4affSfengbojiang }
23322ce4affSfengbojiang 
23422ce4affSfengbojiang static void
remove_netmask(struct rt_addrinfo * info)23522ce4affSfengbojiang remove_netmask(struct rt_addrinfo *info)
23622ce4affSfengbojiang {
23722ce4affSfengbojiang     info->rti_info[RTAX_NETMASK] = NULL;
23822ce4affSfengbojiang     info->rti_flags |= RTF_HOST;
23922ce4affSfengbojiang     info->rti_addrs &= ~RTA_NETMASK;
24022ce4affSfengbojiang }
24122ce4affSfengbojiang 
24222ce4affSfengbojiang static int
cleanup_xaddrs_inet(struct rt_addrinfo * info)24322ce4affSfengbojiang cleanup_xaddrs_inet(struct rt_addrinfo *info)
24422ce4affSfengbojiang {
24522ce4affSfengbojiang     struct sockaddr_in *dst_sa, *mask_sa;
24622ce4affSfengbojiang 
24722ce4affSfengbojiang     /* Check & fixup dst/netmask combination first */
24822ce4affSfengbojiang     dst_sa = (struct sockaddr_in *)info->rti_info[RTAX_DST];
24922ce4affSfengbojiang     mask_sa = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
25022ce4affSfengbojiang 
25122ce4affSfengbojiang     struct in_addr mask = {
25222ce4affSfengbojiang         .s_addr = mask_sa ? mask_sa->sin_addr.s_addr : INADDR_BROADCAST,
25322ce4affSfengbojiang     };
25422ce4affSfengbojiang     struct in_addr dst = {
25522ce4affSfengbojiang         .s_addr = htonl(ntohl(dst_sa->sin_addr.s_addr) & ntohl(mask.s_addr))
25622ce4affSfengbojiang     };
25722ce4affSfengbojiang 
25822ce4affSfengbojiang     if (dst_sa->sin_len < sizeof(struct sockaddr_in)) {
25922ce4affSfengbojiang         printf("dst sin_len too small\n");
26022ce4affSfengbojiang         return (EINVAL);
26122ce4affSfengbojiang     }
26222ce4affSfengbojiang     if (mask_sa && mask_sa->sin_len < sizeof(struct sockaddr_in)) {
26322ce4affSfengbojiang         printf("mask sin_len too small\n");
26422ce4affSfengbojiang         return (EINVAL);
26522ce4affSfengbojiang     }
26622ce4affSfengbojiang     fill_sockaddr_inet(dst_sa, dst);
26722ce4affSfengbojiang 
26822ce4affSfengbojiang     if (mask.s_addr != INADDR_BROADCAST)
26922ce4affSfengbojiang         fill_sockaddr_inet(mask_sa, mask);
27022ce4affSfengbojiang     else
27122ce4affSfengbojiang         remove_netmask(info);
27222ce4affSfengbojiang 
27322ce4affSfengbojiang     /* Check gateway */
27422ce4affSfengbojiang     if (info->rti_info[RTAX_GATEWAY] != NULL)
27522ce4affSfengbojiang         return (cleanup_xaddrs_gateway(info));
27622ce4affSfengbojiang 
27722ce4affSfengbojiang     return (0);
27822ce4affSfengbojiang }
27922ce4affSfengbojiang 
28022ce4affSfengbojiang #ifdef INET6
28122ce4affSfengbojiang static int
cleanup_xaddrs_inet6(struct rt_addrinfo * info)28222ce4affSfengbojiang cleanup_xaddrs_inet6(struct rt_addrinfo *info)
28322ce4affSfengbojiang {
28422ce4affSfengbojiang     struct sockaddr_in6 *dst_sa, *mask_sa;
28522ce4affSfengbojiang     struct in6_addr mask;
28622ce4affSfengbojiang 
28722ce4affSfengbojiang     /* Check & fixup dst/netmask combination first */
28822ce4affSfengbojiang     dst_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_DST];
28922ce4affSfengbojiang     mask_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK];
29022ce4affSfengbojiang 
29122ce4affSfengbojiang     mask = mask_sa ? mask_sa->sin6_addr : in6mask128;
29222ce4affSfengbojiang     IN6_MASK_ADDR(&dst_sa->sin6_addr, &mask);
29322ce4affSfengbojiang 
29422ce4affSfengbojiang     if (dst_sa->sin6_len < sizeof(struct sockaddr_in6)) {
29522ce4affSfengbojiang         printf("dst sin6_len too small\n");
29622ce4affSfengbojiang         return (EINVAL);
29722ce4affSfengbojiang     }
29822ce4affSfengbojiang     if (mask_sa && mask_sa->sin6_len < sizeof(struct sockaddr_in6)) {
29922ce4affSfengbojiang         printf("mask sin6_len too small\n");
30022ce4affSfengbojiang         return (EINVAL);
30122ce4affSfengbojiang     }
30222ce4affSfengbojiang     fill_sockaddr_inet6(dst_sa, &dst_sa->sin6_addr, 0);
30322ce4affSfengbojiang 
30422ce4affSfengbojiang     if (!IN6_ARE_ADDR_EQUAL(&mask, &in6mask128))
30522ce4affSfengbojiang         fill_sockaddr_inet6(mask_sa, &mask, 0);
30622ce4affSfengbojiang     else
30722ce4affSfengbojiang         remove_netmask(info);
30822ce4affSfengbojiang 
30922ce4affSfengbojiang     /* Check gateway */
31022ce4affSfengbojiang     if (info->rti_info[RTAX_GATEWAY] != NULL)
31122ce4affSfengbojiang         return (cleanup_xaddrs_gateway(info));
31222ce4affSfengbojiang 
31322ce4affSfengbojiang     return (0);
31422ce4affSfengbojiang }
31522ce4affSfengbojiang #endif
31622ce4affSfengbojiang 
31722ce4affSfengbojiang static int
cleanup_xaddrs(struct rt_addrinfo * info)31822ce4affSfengbojiang cleanup_xaddrs(struct rt_addrinfo *info)
31922ce4affSfengbojiang {
32022ce4affSfengbojiang     int error = EAFNOSUPPORT;
32122ce4affSfengbojiang 
32222ce4affSfengbojiang     if (info->rti_info[RTAX_DST] == NULL)
32322ce4affSfengbojiang         return (EINVAL);
32422ce4affSfengbojiang 
32522ce4affSfengbojiang     if (info->rti_flags & RTF_LLDATA) {
32622ce4affSfengbojiang         /*
32722ce4affSfengbojiang          * arp(8)/ndp(8) sends RTA_NETMASK for the associated
32822ce4affSfengbojiang          * prefix along with the actual address in RTA_DST.
32922ce4affSfengbojiang          * Remove netmask to avoid unnecessary address masking.
33022ce4affSfengbojiang          */
33122ce4affSfengbojiang         remove_netmask(info);
33222ce4affSfengbojiang     }
33322ce4affSfengbojiang 
33422ce4affSfengbojiang     switch (info->rti_info[RTAX_DST]->sa_family) {
33522ce4affSfengbojiang     case AF_INET:
33622ce4affSfengbojiang         error = cleanup_xaddrs_inet(info);
33722ce4affSfengbojiang         break;
33822ce4affSfengbojiang #ifdef INET6
33922ce4affSfengbojiang     case AF_INET6:
34022ce4affSfengbojiang         error = cleanup_xaddrs_inet6(info);
34122ce4affSfengbojiang         break;
34222ce4affSfengbojiang #endif
34322ce4affSfengbojiang     }
34422ce4affSfengbojiang 
34522ce4affSfengbojiang     return (error);
34622ce4affSfengbojiang }
34722ce4affSfengbojiang 
34822ce4affSfengbojiang static int
rtm_get_jailed(struct rt_addrinfo * info,struct ifnet * ifp,struct nhop_object * nh,union sockaddr_union * saun,struct ucred * cred)34922ce4affSfengbojiang rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp,
35022ce4affSfengbojiang     struct nhop_object *nh, union sockaddr_union *saun, struct ucred *cred)
35122ce4affSfengbojiang {
35222ce4affSfengbojiang #if defined(INET) || defined(INET6)
35322ce4affSfengbojiang     struct epoch_tracker et;
35422ce4affSfengbojiang #endif
35522ce4affSfengbojiang 
35622ce4affSfengbojiang     /* First, see if the returned address is part of the jail. */
35722ce4affSfengbojiang     if (prison_if(cred, nh->nh_ifa->ifa_addr) == 0) {
35822ce4affSfengbojiang         info->rti_info[RTAX_IFA] = nh->nh_ifa->ifa_addr;
35922ce4affSfengbojiang         return (0);
36022ce4affSfengbojiang     }
36122ce4affSfengbojiang 
36222ce4affSfengbojiang     switch (info->rti_info[RTAX_DST]->sa_family) {
36322ce4affSfengbojiang     case AF_INET:
36422ce4affSfengbojiang     {
36522ce4affSfengbojiang         struct in_addr ia;
36622ce4affSfengbojiang         struct ifaddr *ifa;
36722ce4affSfengbojiang         int found;
36822ce4affSfengbojiang 
36922ce4affSfengbojiang         found = 0;
37022ce4affSfengbojiang         /*
37122ce4affSfengbojiang          * Try to find an address on the given outgoing interface
37222ce4affSfengbojiang          * that belongs to the jail.
37322ce4affSfengbojiang          */
37422ce4affSfengbojiang         NET_EPOCH_ENTER(et);
37522ce4affSfengbojiang         CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
37622ce4affSfengbojiang             struct sockaddr *sa;
37722ce4affSfengbojiang             sa = ifa->ifa_addr;
37822ce4affSfengbojiang             if (sa->sa_family != AF_INET)
37922ce4affSfengbojiang                 continue;
38022ce4affSfengbojiang             ia = ((struct sockaddr_in *)sa)->sin_addr;
38122ce4affSfengbojiang             if (prison_check_ip4(cred, &ia) == 0) {
38222ce4affSfengbojiang                 found = 1;
38322ce4affSfengbojiang                 break;
38422ce4affSfengbojiang             }
38522ce4affSfengbojiang         }
38622ce4affSfengbojiang         NET_EPOCH_EXIT(et);
38722ce4affSfengbojiang         if (!found) {
38822ce4affSfengbojiang             /*
38922ce4affSfengbojiang              * As a last resort return the 'default' jail address.
39022ce4affSfengbojiang              */
39122ce4affSfengbojiang             ia = ((struct sockaddr_in *)nh->nh_ifa->ifa_addr)->
39222ce4affSfengbojiang                 sin_addr;
39322ce4affSfengbojiang             if (prison_get_ip4(cred, &ia) != 0)
39422ce4affSfengbojiang                 return (ESRCH);
39522ce4affSfengbojiang         }
39622ce4affSfengbojiang         bzero(&saun->sin, sizeof(struct sockaddr_in));
39722ce4affSfengbojiang         saun->sin.sin_len = sizeof(struct sockaddr_in);
39822ce4affSfengbojiang         saun->sin.sin_family = AF_INET;
39922ce4affSfengbojiang         saun->sin.sin_addr.s_addr = ia.s_addr;
40022ce4affSfengbojiang         info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin;
40122ce4affSfengbojiang         break;
40222ce4affSfengbojiang     }
40322ce4affSfengbojiang #ifdef INET6
40422ce4affSfengbojiang     case AF_INET6:
40522ce4affSfengbojiang     {
40622ce4affSfengbojiang         struct in6_addr ia6;
40722ce4affSfengbojiang         struct ifaddr *ifa;
40822ce4affSfengbojiang         int found;
40922ce4affSfengbojiang 
41022ce4affSfengbojiang         found = 0;
41122ce4affSfengbojiang         /*
41222ce4affSfengbojiang          * Try to find an address on the given outgoing interface
41322ce4affSfengbojiang          * that belongs to the jail.
41422ce4affSfengbojiang          */
41522ce4affSfengbojiang         NET_EPOCH_ENTER(et);
41622ce4affSfengbojiang         CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
41722ce4affSfengbojiang             struct sockaddr *sa;
41822ce4affSfengbojiang             sa = ifa->ifa_addr;
41922ce4affSfengbojiang             if (sa->sa_family != AF_INET6)
42022ce4affSfengbojiang                 continue;
42122ce4affSfengbojiang             bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
42222ce4affSfengbojiang                 &ia6, sizeof(struct in6_addr));
42322ce4affSfengbojiang             if (prison_check_ip6(cred, &ia6) == 0) {
42422ce4affSfengbojiang                 found = 1;
42522ce4affSfengbojiang                 break;
42622ce4affSfengbojiang             }
42722ce4affSfengbojiang         }
42822ce4affSfengbojiang         NET_EPOCH_EXIT(et);
42922ce4affSfengbojiang         if (!found) {
43022ce4affSfengbojiang             /*
43122ce4affSfengbojiang              * As a last resort return the 'default' jail address.
43222ce4affSfengbojiang              */
43322ce4affSfengbojiang             ia6 = ((struct sockaddr_in6 *)nh->nh_ifa->ifa_addr)->
43422ce4affSfengbojiang                 sin6_addr;
43522ce4affSfengbojiang             if (prison_get_ip6(cred, &ia6) != 0)
43622ce4affSfengbojiang                 return (ESRCH);
43722ce4affSfengbojiang         }
43822ce4affSfengbojiang         bzero(&saun->sin6, sizeof(struct sockaddr_in6));
43922ce4affSfengbojiang         saun->sin6.sin6_len = sizeof(struct sockaddr_in6);
44022ce4affSfengbojiang         saun->sin6.sin6_family = AF_INET6;
44122ce4affSfengbojiang         bcopy(&ia6, &saun->sin6.sin6_addr, sizeof(struct in6_addr));
44222ce4affSfengbojiang         if (sa6_recoverscope(&saun->sin6) != 0)
44322ce4affSfengbojiang             return (ESRCH);
44422ce4affSfengbojiang         info->rti_info[RTAX_IFA] = (struct sockaddr *)&saun->sin6;
44522ce4affSfengbojiang         break;
44622ce4affSfengbojiang     }
44722ce4affSfengbojiang #endif
44822ce4affSfengbojiang     default:
44922ce4affSfengbojiang         return (ESRCH);
45022ce4affSfengbojiang     }
45122ce4affSfengbojiang     return (0);
45222ce4affSfengbojiang }
45322ce4affSfengbojiang 
45422ce4affSfengbojiang static int
fill_blackholeinfo(struct rt_addrinfo * info,union sockaddr_union * saun)45522ce4affSfengbojiang fill_blackholeinfo(struct rt_addrinfo *info, union sockaddr_union *saun)
45622ce4affSfengbojiang {
45722ce4affSfengbojiang     struct ifaddr *ifa;
45822ce4affSfengbojiang     sa_family_t saf;
45922ce4affSfengbojiang 
46022ce4affSfengbojiang     if (V_loif == NULL) {
46122ce4affSfengbojiang         printf("Unable to add blackhole/reject nhop without loopback");
46222ce4affSfengbojiang         return (ENOTSUP);
46322ce4affSfengbojiang     }
46422ce4affSfengbojiang     info->rti_ifp = V_loif;
46522ce4affSfengbojiang 
46622ce4affSfengbojiang     saf = info->rti_info[RTAX_DST]->sa_family;
46722ce4affSfengbojiang 
46822ce4affSfengbojiang     CK_STAILQ_FOREACH(ifa, &info->rti_ifp->if_addrhead, ifa_link) {
46922ce4affSfengbojiang         if (ifa->ifa_addr->sa_family == saf) {
47022ce4affSfengbojiang             info->rti_ifa = ifa;
47122ce4affSfengbojiang             break;
47222ce4affSfengbojiang         }
47322ce4affSfengbojiang     }
47422ce4affSfengbojiang     if (info->rti_ifa == NULL)
47522ce4affSfengbojiang         return (ENOTSUP);
47622ce4affSfengbojiang 
47722ce4affSfengbojiang     bzero(saun, sizeof(union sockaddr_union));
47822ce4affSfengbojiang     switch (saf) {
47922ce4affSfengbojiang     case AF_INET:
48022ce4affSfengbojiang         saun->sin.sin_family = AF_INET;
48122ce4affSfengbojiang         saun->sin.sin_len = sizeof(struct sockaddr_in);
48222ce4affSfengbojiang         saun->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
48322ce4affSfengbojiang         break;
48422ce4affSfengbojiang #ifdef INET6
48522ce4affSfengbojiang     case AF_INET6:
48622ce4affSfengbojiang         saun->sin6.sin6_family = AF_INET6;
48722ce4affSfengbojiang         saun->sin6.sin6_len = sizeof(struct sockaddr_in6);
48822ce4affSfengbojiang         saun->sin6.sin6_addr = in6addr_loopback;
48922ce4affSfengbojiang         break;
49022ce4affSfengbojiang #endif
49122ce4affSfengbojiang     default:
49222ce4affSfengbojiang         return (ENOTSUP);
49322ce4affSfengbojiang     }
49422ce4affSfengbojiang     info->rti_info[RTAX_GATEWAY] = &saun->sa;
49522ce4affSfengbojiang     info->rti_flags |= RTF_GATEWAY;
49622ce4affSfengbojiang 
49722ce4affSfengbojiang     return (0);
49822ce4affSfengbojiang }
49922ce4affSfengbojiang 
50022ce4affSfengbojiang /*
50122ce4affSfengbojiang  * Fills in @info based on userland-provided @rtm message.
50222ce4affSfengbojiang  *
50322ce4affSfengbojiang  * Returns 0 on success.
50422ce4affSfengbojiang  */
50522ce4affSfengbojiang static int
fill_addrinfo(struct rt_msghdr * rtm,int len,u_int fibnum,struct rt_addrinfo * info)50622ce4affSfengbojiang fill_addrinfo(struct rt_msghdr *rtm, int len, u_int fibnum, struct rt_addrinfo *info)
50722ce4affSfengbojiang {
50822ce4affSfengbojiang     int error;
50922ce4affSfengbojiang     sa_family_t saf;
51022ce4affSfengbojiang 
51122ce4affSfengbojiang     rtm->rtm_pid = curproc->p_pid;
51222ce4affSfengbojiang     info->rti_addrs = rtm->rtm_addrs;
51322ce4affSfengbojiang 
51422ce4affSfengbojiang     info->rti_mflags = rtm->rtm_inits;
51522ce4affSfengbojiang     info->rti_rmx = &rtm->rtm_rmx;
51622ce4affSfengbojiang 
51722ce4affSfengbojiang     /*
51822ce4affSfengbojiang      * rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6
51922ce4affSfengbojiang      * link-local address because rtrequest requires addresses with
52022ce4affSfengbojiang      * embedded scope id.
52122ce4affSfengbojiang      */
52222ce4affSfengbojiang     if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, info))
52322ce4affSfengbojiang         return (EINVAL);
52422ce4affSfengbojiang 
52522ce4affSfengbojiang     if (rtm->rtm_flags & RTF_RNH_LOCKED)
52622ce4affSfengbojiang         return (EINVAL);
52722ce4affSfengbojiang     info->rti_flags = rtm->rtm_flags;
52822ce4affSfengbojiang     error = cleanup_xaddrs(info);
52922ce4affSfengbojiang     if (error != 0)
53022ce4affSfengbojiang         return (error);
53122ce4affSfengbojiang     saf = info->rti_info[RTAX_DST]->sa_family;
53222ce4affSfengbojiang     /*
53322ce4affSfengbojiang      * Verify that the caller has the appropriate privilege; RTM_GET
53422ce4affSfengbojiang      * is the only operation the non-superuser is allowed.
53522ce4affSfengbojiang      */
53622ce4affSfengbojiang     if (rtm->rtm_type != RTM_GET) {
53722ce4affSfengbojiang         error = priv_check(curthread, PRIV_NET_ROUTE);
53822ce4affSfengbojiang         if (error != 0)
53922ce4affSfengbojiang             return (error);
54022ce4affSfengbojiang     }
54122ce4affSfengbojiang 
54222ce4affSfengbojiang     /*
54322ce4affSfengbojiang      * The given gateway address may be an interface address.
54422ce4affSfengbojiang      * For example, issuing a "route change" command on a route
54522ce4affSfengbojiang      * entry that was created from a tunnel, and the gateway
54622ce4affSfengbojiang      * address given is the local end point. In this case the
54722ce4affSfengbojiang      * RTF_GATEWAY flag must be cleared or the destination will
54822ce4affSfengbojiang      * not be reachable even though there is no error message.
54922ce4affSfengbojiang      */
55022ce4affSfengbojiang     if (info->rti_info[RTAX_GATEWAY] != NULL &&
55122ce4affSfengbojiang         info->rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
55222ce4affSfengbojiang         struct rt_addrinfo ginfo;
55322ce4affSfengbojiang         struct sockaddr *gdst;
55422ce4affSfengbojiang         struct sockaddr_storage ss;
55522ce4affSfengbojiang 
55622ce4affSfengbojiang         bzero(&ginfo, sizeof(ginfo));
55722ce4affSfengbojiang         bzero(&ss, sizeof(ss));
55822ce4affSfengbojiang         ss.ss_len = sizeof(ss);
55922ce4affSfengbojiang 
56022ce4affSfengbojiang         ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
56122ce4affSfengbojiang         gdst = info->rti_info[RTAX_GATEWAY];
56222ce4affSfengbojiang 
56322ce4affSfengbojiang         /*
56422ce4affSfengbojiang          * A host route through the loopback interface is
56522ce4affSfengbojiang          * installed for each interface adddress. In pre 8.0
56622ce4affSfengbojiang          * releases the interface address of a PPP link type
56722ce4affSfengbojiang          * is not reachable locally. This behavior is fixed as
56822ce4affSfengbojiang          * part of the new L2/L3 redesign and rewrite work. The
56922ce4affSfengbojiang          * signature of this interface address route is the
57022ce4affSfengbojiang          * AF_LINK sa_family type of the gateway, and the
57122ce4affSfengbojiang          * rt_ifp has the IFF_LOOPBACK flag set.
57222ce4affSfengbojiang          */
57322ce4affSfengbojiang         if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
57422ce4affSfengbojiang             if (ss.ss_family == AF_LINK &&
57522ce4affSfengbojiang                 ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
57622ce4affSfengbojiang                 info->rti_flags &= ~RTF_GATEWAY;
57722ce4affSfengbojiang                 info->rti_flags |= RTF_GWFLAG_COMPAT;
57822ce4affSfengbojiang             }
57922ce4affSfengbojiang             rib_free_info(&ginfo);
58022ce4affSfengbojiang         }
58122ce4affSfengbojiang     }
58222ce4affSfengbojiang 
58322ce4affSfengbojiang     return (0);
58422ce4affSfengbojiang }
58522ce4affSfengbojiang 
58622ce4affSfengbojiang /*
58722ce4affSfengbojiang  * Returns pointer to array of nexthops with weights for
58822ce4affSfengbojiang  * given @nhg. Stores number of items in the array into @pnum_nhops.
58922ce4affSfengbojiang  */
59022ce4affSfengbojiang struct weightened_nhop *
nhgrp_get_nhops(struct nhgrp_object * nhg,uint32_t * pnum_nhops)59122ce4affSfengbojiang nhgrp_get_nhops(struct nhgrp_object *nhg, uint32_t *pnum_nhops)
59222ce4affSfengbojiang {
59322ce4affSfengbojiang 	struct nhgrp_priv *nhg_priv;
59422ce4affSfengbojiang 
59522ce4affSfengbojiang 	KASSERT(((nhg->nhg_flags & MPF_MULTIPATH) != 0), ("nhop is not mpath"));
59622ce4affSfengbojiang 
59722ce4affSfengbojiang 	nhg_priv = NHGRP_PRIV(nhg);
59822ce4affSfengbojiang 	*pnum_nhops = nhg_priv->nhg_nh_count;
59922ce4affSfengbojiang 
60022ce4affSfengbojiang 	return (nhg_priv->nhg_nh_weights);
60122ce4affSfengbojiang }
60222ce4affSfengbojiang 
60322ce4affSfengbojiang static struct nhop_object *
select_nhop(struct nhop_object * nh,const struct sockaddr * gw)60422ce4affSfengbojiang select_nhop(struct nhop_object *nh, const struct sockaddr *gw)
60522ce4affSfengbojiang {
60622ce4affSfengbojiang 	if (!NH_IS_NHGRP(nh))
60722ce4affSfengbojiang 		return (nh);
60822ce4affSfengbojiang #ifdef ROUTE_MPATH
60922ce4affSfengbojiang 	struct weightened_nhop *wn;
61022ce4affSfengbojiang 	uint32_t num_nhops;
61122ce4affSfengbojiang 	wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops);
61222ce4affSfengbojiang 	if (gw == NULL)
61322ce4affSfengbojiang 		return (wn[0].nh);
61422ce4affSfengbojiang 	for (int i = 0; i < num_nhops; i++) {
61522ce4affSfengbojiang 		if (match_nhop_gw(wn[i].nh, gw))
61622ce4affSfengbojiang 			return (wn[i].nh);
61722ce4affSfengbojiang 	}
61822ce4affSfengbojiang #endif
61922ce4affSfengbojiang 	return (NULL);
62022ce4affSfengbojiang }
62122ce4affSfengbojiang 
62222ce4affSfengbojiang /*
62322ce4affSfengbojiang  * Handles RTM_GET message from routing socket, returning matching rt.
62422ce4affSfengbojiang  *
62522ce4affSfengbojiang  * Returns:
62622ce4affSfengbojiang  * 0 on success, with locked and referenced matching rt in @rt_nrt
62722ce4affSfengbojiang  * errno of failure
62822ce4affSfengbojiang  */
62922ce4affSfengbojiang static int
handle_rtm_get(struct rt_addrinfo * info,u_int fibnum,struct rt_msghdr * rtm,struct rib_cmd_info * rc)63022ce4affSfengbojiang handle_rtm_get(struct rt_addrinfo *info, u_int fibnum,
63122ce4affSfengbojiang     struct rt_msghdr *rtm, struct rib_cmd_info *rc)
63222ce4affSfengbojiang {
63322ce4affSfengbojiang 	RIB_RLOCK_TRACKER;
63422ce4affSfengbojiang 	struct rib_head *rnh;
63522ce4affSfengbojiang 	struct nhop_object *nh;
63622ce4affSfengbojiang 	sa_family_t saf;
63722ce4affSfengbojiang 
63822ce4affSfengbojiang 	saf = info->rti_info[RTAX_DST]->sa_family;
63922ce4affSfengbojiang 
64022ce4affSfengbojiang 	rnh = rt_tables_get_rnh(fibnum, saf);
64122ce4affSfengbojiang 	if (rnh == NULL)
64222ce4affSfengbojiang 		return (EAFNOSUPPORT);
64322ce4affSfengbojiang 
64422ce4affSfengbojiang 	RIB_RLOCK(rnh);
64522ce4affSfengbojiang 
64622ce4affSfengbojiang 	/*
64722ce4affSfengbojiang 	 * By (implicit) convention host route (one without netmask)
64822ce4affSfengbojiang 	 * means longest-prefix-match request and the route with netmask
64922ce4affSfengbojiang 	 * means exact-match lookup.
65022ce4affSfengbojiang 	 * As cleanup_xaddrs() cleans up info flags&addrs for the /32,/128
65122ce4affSfengbojiang 	 * prefixes, use original data to check for the netmask presence.
65222ce4affSfengbojiang 	 */
65322ce4affSfengbojiang 	if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
65422ce4affSfengbojiang 		/*
65522ce4affSfengbojiang 		 * Provide longest prefix match for
65622ce4affSfengbojiang 		 * address lookup (no mask).
65722ce4affSfengbojiang 		 * 'route -n get addr'
65822ce4affSfengbojiang 		 */
65922ce4affSfengbojiang 		rc->rc_rt = (struct rtentry *) rnh->rnh_matchaddr(
66022ce4affSfengbojiang 		    info->rti_info[RTAX_DST], &rnh->head);
66122ce4affSfengbojiang 	} else
66222ce4affSfengbojiang 		rc->rc_rt = (struct rtentry *) rnh->rnh_lookup(
66322ce4affSfengbojiang 		    info->rti_info[RTAX_DST],
66422ce4affSfengbojiang 		    info->rti_info[RTAX_NETMASK], &rnh->head);
66522ce4affSfengbojiang 
66622ce4affSfengbojiang 	if (rc->rc_rt == NULL) {
66722ce4affSfengbojiang 		RIB_RUNLOCK(rnh);
66822ce4affSfengbojiang 		return (ESRCH);
66922ce4affSfengbojiang 	}
67022ce4affSfengbojiang 
67122ce4affSfengbojiang 	nh = select_nhop(rt_get_raw_nhop(rc->rc_rt), info->rti_info[RTAX_GATEWAY]);
67222ce4affSfengbojiang 	if (nh == NULL) {
67322ce4affSfengbojiang 		RIB_RUNLOCK(rnh);
67422ce4affSfengbojiang 		return (ESRCH);
67522ce4affSfengbojiang 	}
67622ce4affSfengbojiang 	/*
67722ce4affSfengbojiang 	 * If performing proxied L2 entry insertion, and
67822ce4affSfengbojiang 	 * the actual PPP host entry is found, perform
67922ce4affSfengbojiang 	 * another search to retrieve the prefix route of
68022ce4affSfengbojiang 	 * the local end point of the PPP link.
68122ce4affSfengbojiang 	 * TODO: move this logic to userland.
68222ce4affSfengbojiang 	 */
68322ce4affSfengbojiang 	if (rtm->rtm_flags & RTF_ANNOUNCE) {
68422ce4affSfengbojiang 		struct sockaddr laddr;
68522ce4affSfengbojiang 
68622ce4affSfengbojiang 		if (nh->nh_ifp != NULL &&
68722ce4affSfengbojiang 		    nh->nh_ifp->if_type == IFT_PROPVIRTUAL) {
68822ce4affSfengbojiang 			struct ifaddr *ifa;
68922ce4affSfengbojiang 
69022ce4affSfengbojiang 			ifa = ifa_ifwithnet(info->rti_info[RTAX_DST], 1,
69122ce4affSfengbojiang 					RT_ALL_FIBS);
69222ce4affSfengbojiang 			if (ifa != NULL)
69322ce4affSfengbojiang 				rt_maskedcopy(ifa->ifa_addr,
69422ce4affSfengbojiang 					      &laddr,
69522ce4affSfengbojiang 					      ifa->ifa_netmask);
69622ce4affSfengbojiang 		} else
69722ce4affSfengbojiang 			rt_maskedcopy(nh->nh_ifa->ifa_addr,
69822ce4affSfengbojiang 				      &laddr,
69922ce4affSfengbojiang 				      nh->nh_ifa->ifa_netmask);
70022ce4affSfengbojiang 		/*
70122ce4affSfengbojiang 		 * refactor rt and no lock operation necessary
70222ce4affSfengbojiang 		 */
70322ce4affSfengbojiang 		rc->rc_rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr,
70422ce4affSfengbojiang 		    &rnh->head);
70522ce4affSfengbojiang 		if (rc->rc_rt == NULL) {
70622ce4affSfengbojiang 			RIB_RUNLOCK(rnh);
70722ce4affSfengbojiang 			return (ESRCH);
70822ce4affSfengbojiang 		}
70922ce4affSfengbojiang 		nh = select_nhop(rt_get_raw_nhop(rc->rc_rt), info->rti_info[RTAX_GATEWAY]);
71022ce4affSfengbojiang 		if (nh == NULL) {
71122ce4affSfengbojiang 			RIB_RUNLOCK(rnh);
71222ce4affSfengbojiang 			return (ESRCH);
71322ce4affSfengbojiang 		}
71422ce4affSfengbojiang 	}
71522ce4affSfengbojiang 	rc->rc_nh_new = nh;
71622ce4affSfengbojiang 	rc->rc_nh_weight = rc->rc_rt->rt_weight;
71722ce4affSfengbojiang 	RIB_RUNLOCK(rnh);
71822ce4affSfengbojiang 
71922ce4affSfengbojiang 	return (0);
72022ce4affSfengbojiang }
72122ce4affSfengbojiang 
722144c6bcdSlogwang /*
723144c6bcdSlogwang  * Writes information related to @rtinfo object to preallocated buffer.
724144c6bcdSlogwang  * Stores needed size in @plen. If @w is NULL, calculates size without
725144c6bcdSlogwang  * writing.
726144c6bcdSlogwang  * Used for sysctl dumps and rtsock answers (RTM_DEL/RTM_GET) generation.
727144c6bcdSlogwang  *
728144c6bcdSlogwang  * Returns 0 on success.
729144c6bcdSlogwang  *
730144c6bcdSlogwang  */
731144c6bcdSlogwang static int
rtsock_msg_buffer(int type,struct rt_addrinfo * rtinfo,struct walkarg * w,int * plen)732144c6bcdSlogwang rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int *plen)
733144c6bcdSlogwang {
734144c6bcdSlogwang     int i;
735144c6bcdSlogwang     int len, buflen = 0, dlen;
736144c6bcdSlogwang     caddr_t cp = NULL;
737144c6bcdSlogwang     struct rt_msghdr *rtm = NULL;
738144c6bcdSlogwang #ifdef INET6
739144c6bcdSlogwang     struct sockaddr_storage ss;
740144c6bcdSlogwang     struct sockaddr_in6 *sin6;
741144c6bcdSlogwang #endif
742144c6bcdSlogwang 
743144c6bcdSlogwang     switch (type) {
744144c6bcdSlogwang 
745144c6bcdSlogwang     case RTM_DELADDR:
746144c6bcdSlogwang     case RTM_NEWADDR:
747144c6bcdSlogwang         if (w != NULL && w->w_op == NET_RT_IFLISTL) {
748144c6bcdSlogwang #ifdef COMPAT_FREEBSD32
749144c6bcdSlogwang             if (w->w_req->flags & SCTL_MASK32)
750144c6bcdSlogwang                 len = sizeof(struct ifa_msghdrl32);
751144c6bcdSlogwang             else
752144c6bcdSlogwang #endif
753144c6bcdSlogwang                 len = sizeof(struct ifa_msghdrl);
754144c6bcdSlogwang         } else
755144c6bcdSlogwang             len = sizeof(struct ifa_msghdr);
756144c6bcdSlogwang         break;
757144c6bcdSlogwang 
758144c6bcdSlogwang     case RTM_IFINFO:
759144c6bcdSlogwang #ifdef COMPAT_FREEBSD32
760144c6bcdSlogwang         if (w != NULL && w->w_req->flags & SCTL_MASK32) {
761144c6bcdSlogwang             if (w->w_op == NET_RT_IFLISTL)
762144c6bcdSlogwang                 len = sizeof(struct if_msghdrl32);
763144c6bcdSlogwang             else
764144c6bcdSlogwang                 len = sizeof(struct if_msghdr32);
765144c6bcdSlogwang             break;
766144c6bcdSlogwang         }
767144c6bcdSlogwang #endif
768144c6bcdSlogwang         if (w != NULL && w->w_op == NET_RT_IFLISTL)
769144c6bcdSlogwang             len = sizeof(struct if_msghdrl);
770144c6bcdSlogwang         else
771144c6bcdSlogwang             len = sizeof(struct if_msghdr);
772144c6bcdSlogwang         break;
773144c6bcdSlogwang 
774144c6bcdSlogwang     case RTM_NEWMADDR:
775144c6bcdSlogwang         len = sizeof(struct ifma_msghdr);
776144c6bcdSlogwang         break;
777144c6bcdSlogwang 
778144c6bcdSlogwang     default:
779144c6bcdSlogwang         len = sizeof(struct rt_msghdr);
780144c6bcdSlogwang     }
781144c6bcdSlogwang 
782144c6bcdSlogwang     if (w != NULL) {
783144c6bcdSlogwang         rtm = (struct rt_msghdr *)w->w_tmem;
784144c6bcdSlogwang         buflen = w->w_tmemsize - len;
785144c6bcdSlogwang         cp = (caddr_t)w->w_tmem + len;
786144c6bcdSlogwang     }
787144c6bcdSlogwang 
788144c6bcdSlogwang     rtinfo->rti_addrs = 0;
789144c6bcdSlogwang     for (i = 0; i < RTAX_MAX; i++) {
790144c6bcdSlogwang         struct sockaddr *sa;
791144c6bcdSlogwang 
792144c6bcdSlogwang         if ((sa = rtinfo->rti_info[i]) == NULL)
793144c6bcdSlogwang             continue;
794144c6bcdSlogwang         rtinfo->rti_addrs |= (1 << i);
795144c6bcdSlogwang         dlen = SA_SIZE(sa);
796144c6bcdSlogwang         if (cp != NULL && buflen >= dlen) {
797144c6bcdSlogwang #ifdef INET6
79822ce4affSfengbojiang             if (sa->sa_family == AF_INET6) {
799144c6bcdSlogwang                 sin6 = (struct sockaddr_in6 *)&ss;
800144c6bcdSlogwang                 bcopy(sa, sin6, sizeof(*sin6));
801144c6bcdSlogwang                 if (sa6_recoverscope(sin6) == 0)
802144c6bcdSlogwang                     sa = (struct sockaddr *)sin6;
803144c6bcdSlogwang             }
804144c6bcdSlogwang #endif
805144c6bcdSlogwang             bcopy((caddr_t)sa, cp, (unsigned)dlen);
806144c6bcdSlogwang             cp += dlen;
807144c6bcdSlogwang             buflen -= dlen;
808144c6bcdSlogwang         } else if (cp != NULL) {
809144c6bcdSlogwang             /*
810144c6bcdSlogwang              * Buffer too small. Count needed size
811144c6bcdSlogwang              * and return with error.
812144c6bcdSlogwang              */
813144c6bcdSlogwang             cp = NULL;
814144c6bcdSlogwang         }
815144c6bcdSlogwang 
816144c6bcdSlogwang         len += dlen;
817144c6bcdSlogwang     }
818144c6bcdSlogwang 
819144c6bcdSlogwang     if (cp != NULL) {
820144c6bcdSlogwang         dlen = ALIGN(len) - len;
821144c6bcdSlogwang         if (buflen < dlen)
822144c6bcdSlogwang             cp = NULL;
823144c6bcdSlogwang         else
824144c6bcdSlogwang             buflen -= dlen;
825144c6bcdSlogwang     }
826144c6bcdSlogwang     len = ALIGN(len);
827144c6bcdSlogwang 
828144c6bcdSlogwang     if (cp != NULL) {
829144c6bcdSlogwang         /* fill header iff buffer is large enough */
830144c6bcdSlogwang         rtm->rtm_version = RTM_VERSION;
831144c6bcdSlogwang         rtm->rtm_type = type;
832144c6bcdSlogwang         rtm->rtm_msglen = len;
833144c6bcdSlogwang     }
834144c6bcdSlogwang 
835144c6bcdSlogwang     *plen = len;
836144c6bcdSlogwang 
837144c6bcdSlogwang     if (w != NULL && cp == NULL)
838144c6bcdSlogwang         return (ENOBUFS);
839144c6bcdSlogwang 
840144c6bcdSlogwang     return (0);
841144c6bcdSlogwang }
842144c6bcdSlogwang 
84322ce4affSfengbojiang #if 0
844144c6bcdSlogwang /*
845144c6bcdSlogwang  * Fill in @dmask with valid netmask leaving original @smask
846144c6bcdSlogwang  * intact. Mostly used with radix netmasks.
847144c6bcdSlogwang  */
84822ce4affSfengbojiang struct sockaddr *
84922ce4affSfengbojiang rtsock_fix_netmask(const struct sockaddr *dst, const struct sockaddr *smask,
850144c6bcdSlogwang     struct sockaddr_storage *dmask)
851144c6bcdSlogwang {
852144c6bcdSlogwang     if (dst == NULL || smask == NULL)
853144c6bcdSlogwang         return (NULL);
854144c6bcdSlogwang 
855144c6bcdSlogwang     memset(dmask, 0, dst->sa_len);
856144c6bcdSlogwang     memcpy(dmask, smask, smask->sa_len);
857144c6bcdSlogwang     dmask->ss_len = dst->sa_len;
858144c6bcdSlogwang     dmask->ss_family = dst->sa_family;
859144c6bcdSlogwang 
860144c6bcdSlogwang     return ((struct sockaddr *)dmask);
861144c6bcdSlogwang }
86222ce4affSfengbojiang #endif
863144c6bcdSlogwang 
864144c6bcdSlogwang static void
rt_getmetrics(const struct rtentry * rt,const struct nhop_object * nh,struct rt_metrics * out)86522ce4affSfengbojiang rt_getmetrics(const struct rtentry *rt, const struct nhop_object *nh,
86622ce4affSfengbojiang     struct rt_metrics *out)
867144c6bcdSlogwang {
868144c6bcdSlogwang 
869144c6bcdSlogwang     bzero(out, sizeof(*out));
87022ce4affSfengbojiang     out->rmx_mtu = nh->nh_mtu;
871144c6bcdSlogwang     out->rmx_weight = rt->rt_weight;
87222ce4affSfengbojiang     out->rmx_nhidx = nhop_get_idx(nh);
873144c6bcdSlogwang     /* Kernel -> userland timebase conversion. */
874144c6bcdSlogwang     out->rmx_expire = rt->rt_expire ?
875144c6bcdSlogwang         rt->rt_expire - time_uptime + time_second : 0;
876144c6bcdSlogwang }
877144c6bcdSlogwang 
87822ce4affSfengbojiang static void
init_sockaddrs_family(int family,struct sockaddr * dst,struct sockaddr * mask)87922ce4affSfengbojiang init_sockaddrs_family(int family, struct sockaddr *dst, struct sockaddr *mask)
88022ce4affSfengbojiang {
88122ce4affSfengbojiang     if (family == AF_INET) {
88222ce4affSfengbojiang         struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
88322ce4affSfengbojiang         struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
88422ce4affSfengbojiang 
88522ce4affSfengbojiang         bzero(dst4, sizeof(struct sockaddr_in));
88622ce4affSfengbojiang         bzero(mask4, sizeof(struct sockaddr_in));
88722ce4affSfengbojiang 
88822ce4affSfengbojiang         dst4->sin_family = AF_INET;
88922ce4affSfengbojiang         dst4->sin_len = sizeof(struct sockaddr_in);
89022ce4affSfengbojiang         mask4->sin_family = AF_INET;
89122ce4affSfengbojiang         mask4->sin_len = sizeof(struct sockaddr_in);
89222ce4affSfengbojiang     }
89322ce4affSfengbojiang #ifdef INET6
89422ce4affSfengbojiang     if (family == AF_INET6) {
89522ce4affSfengbojiang         struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
89622ce4affSfengbojiang         struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
89722ce4affSfengbojiang 
89822ce4affSfengbojiang         bzero(dst6, sizeof(struct sockaddr_in6));
89922ce4affSfengbojiang         bzero(mask6, sizeof(struct sockaddr_in6));
90022ce4affSfengbojiang 
90122ce4affSfengbojiang         dst6->sin6_family = AF_INET6;
90222ce4affSfengbojiang         dst6->sin6_len = sizeof(struct sockaddr_in6);
90322ce4affSfengbojiang         mask6->sin6_family = AF_INET6;
90422ce4affSfengbojiang         mask6->sin6_len = sizeof(struct sockaddr_in6);
90522ce4affSfengbojiang     }
90622ce4affSfengbojiang #endif
90722ce4affSfengbojiang }
90822ce4affSfengbojiang 
90922ce4affSfengbojiang static void
export_rtaddrs(const struct rtentry * rt,struct sockaddr * dst,struct sockaddr * mask)91022ce4affSfengbojiang export_rtaddrs(const struct rtentry *rt, struct sockaddr *dst,
91122ce4affSfengbojiang     struct sockaddr *mask)
91222ce4affSfengbojiang {
91322ce4affSfengbojiang     if (dst->sa_family == AF_INET) {
91422ce4affSfengbojiang         struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
91522ce4affSfengbojiang         struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
91622ce4affSfengbojiang         uint32_t scopeid = 0;
91722ce4affSfengbojiang         rt_get_inet_prefix_pmask(rt, &dst4->sin_addr, &mask4->sin_addr,
91822ce4affSfengbojiang             &scopeid);
91922ce4affSfengbojiang         return;
92022ce4affSfengbojiang     }
92122ce4affSfengbojiang #ifdef INET6
92222ce4affSfengbojiang     if (dst->sa_family == AF_INET6) {
92322ce4affSfengbojiang         struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
92422ce4affSfengbojiang         struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
92522ce4affSfengbojiang         uint32_t scopeid = 0;
92622ce4affSfengbojiang         rt_get_inet6_prefix_pmask(rt, &dst6->sin6_addr,
92722ce4affSfengbojiang             &mask6->sin6_addr, &scopeid);
92822ce4affSfengbojiang         dst6->sin6_scope_id = scopeid;
92922ce4affSfengbojiang         return;
93022ce4affSfengbojiang     }
93122ce4affSfengbojiang #endif
93222ce4affSfengbojiang }
93322ce4affSfengbojiang 
93422ce4affSfengbojiang static int
update_rtm_from_rc(struct rt_addrinfo * info,struct rt_msghdr ** prtm,int alloc_len,struct rib_cmd_info * rc,struct nhop_object * nh,unsigned maxlen)93522ce4affSfengbojiang update_rtm_from_rc(struct rt_addrinfo *info, struct rt_msghdr **prtm,
93622ce4affSfengbojiang     int alloc_len, struct rib_cmd_info *rc, struct nhop_object *nh, unsigned maxlen)
93722ce4affSfengbojiang {
93822ce4affSfengbojiang     struct walkarg w;
93922ce4affSfengbojiang     union sockaddr_union saun;
94022ce4affSfengbojiang     struct rt_msghdr *rtm, *orig_rtm = NULL;
94122ce4affSfengbojiang     struct ifnet *ifp;
94222ce4affSfengbojiang     int error, len;
94322ce4affSfengbojiang 
94422ce4affSfengbojiang     rtm = *prtm;
94522ce4affSfengbojiang     union sockaddr_union sa_dst, sa_mask;
94622ce4affSfengbojiang     int family = info->rti_info[RTAX_DST]->sa_family;
94722ce4affSfengbojiang     init_sockaddrs_family(family, &sa_dst.sa, &sa_mask.sa);
94822ce4affSfengbojiang     export_rtaddrs(rc->rc_rt, &sa_dst.sa, &sa_mask.sa);
94922ce4affSfengbojiang 
95022ce4affSfengbojiang     info->rti_info[RTAX_DST] = &sa_dst.sa;
95122ce4affSfengbojiang     info->rti_info[RTAX_NETMASK] = rt_is_host(rc->rc_rt) ? NULL : &sa_mask.sa;
95222ce4affSfengbojiang     info->rti_info[RTAX_GATEWAY] = &nh->gw_sa;
95322ce4affSfengbojiang     info->rti_info[RTAX_GENMASK] = 0;
95422ce4affSfengbojiang     ifp = nh->nh_ifp;
95522ce4affSfengbojiang     if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
95622ce4affSfengbojiang         if (ifp) {
95722ce4affSfengbojiang             info->rti_info[RTAX_IFP] =
95822ce4affSfengbojiang                 ifp->if_addr->ifa_addr;
95922ce4affSfengbojiang             error = rtm_get_jailed(info, ifp, nh,
96022ce4affSfengbojiang                 &saun, curthread->td_ucred);
96122ce4affSfengbojiang             if (error != 0)
96222ce4affSfengbojiang                 return (error);
96322ce4affSfengbojiang             if (ifp->if_flags & IFF_POINTOPOINT)
96422ce4affSfengbojiang                 info->rti_info[RTAX_BRD] =
96522ce4affSfengbojiang                     nh->nh_ifa->ifa_dstaddr;
96622ce4affSfengbojiang             rtm->rtm_index = ifp->if_index;
96722ce4affSfengbojiang         } else {
96822ce4affSfengbojiang             info->rti_info[RTAX_IFP] = NULL;
96922ce4affSfengbojiang             info->rti_info[RTAX_IFA] = NULL;
97022ce4affSfengbojiang         }
97122ce4affSfengbojiang     } else if (ifp != NULL)
97222ce4affSfengbojiang         rtm->rtm_index = ifp->if_index;
97322ce4affSfengbojiang 
97422ce4affSfengbojiang     /* Check if we need to realloc storage */
97522ce4affSfengbojiang     rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len);
97622ce4affSfengbojiang     if (len > maxlen) {
97722ce4affSfengbojiang         return (ENOBUFS);
97822ce4affSfengbojiang     }
97922ce4affSfengbojiang 
98022ce4affSfengbojiang     if (len > alloc_len) {
98122ce4affSfengbojiang         struct rt_msghdr *tmp_rtm;
98222ce4affSfengbojiang 
98322ce4affSfengbojiang         tmp_rtm = malloc(len, M_TEMP, M_NOWAIT);
98422ce4affSfengbojiang         if (tmp_rtm == NULL)
98522ce4affSfengbojiang             return (ENOBUFS);
98622ce4affSfengbojiang         bcopy(rtm, tmp_rtm, rtm->rtm_msglen);
98722ce4affSfengbojiang         orig_rtm = rtm;
98822ce4affSfengbojiang         rtm = tmp_rtm;
98922ce4affSfengbojiang         alloc_len = len;
99022ce4affSfengbojiang 
99122ce4affSfengbojiang         /*
99222ce4affSfengbojiang          * Delay freeing original rtm as info contains
99322ce4affSfengbojiang          * data referencing it.
99422ce4affSfengbojiang          */
99522ce4affSfengbojiang     }
99622ce4affSfengbojiang 
99722ce4affSfengbojiang     w.w_tmem = (caddr_t)rtm;
99822ce4affSfengbojiang     w.w_tmemsize = alloc_len;
99922ce4affSfengbojiang     rtsock_msg_buffer(rtm->rtm_type, info, &w, &len);
100022ce4affSfengbojiang 
100122ce4affSfengbojiang     rtm->rtm_flags = rc->rc_rt->rte_flags | nhop_get_rtflags(nh);
100222ce4affSfengbojiang     if (rtm->rtm_flags & RTF_GWFLAG_COMPAT)
100322ce4affSfengbojiang         rtm->rtm_flags = RTF_GATEWAY |
100422ce4affSfengbojiang             (rtm->rtm_flags & ~RTF_GWFLAG_COMPAT);
100522ce4affSfengbojiang     rt_getmetrics(rc->rc_rt, nh, &rtm->rtm_rmx);
100622ce4affSfengbojiang     rtm->rtm_rmx.rmx_weight = rc->rc_nh_weight;
100722ce4affSfengbojiang     rtm->rtm_addrs = info->rti_addrs;
100822ce4affSfengbojiang 
100922ce4affSfengbojiang     if (orig_rtm != NULL)
101022ce4affSfengbojiang         free(orig_rtm, M_TEMP);
101122ce4affSfengbojiang     *prtm = rtm;
101222ce4affSfengbojiang 
101322ce4affSfengbojiang     return (0);
101422ce4affSfengbojiang }
101522ce4affSfengbojiang 
101622ce4affSfengbojiang /*
101722ce4affSfengbojiang  * Checks if rte can be exported v.r.t jails/vnets.
101822ce4affSfengbojiang  *
101922ce4affSfengbojiang  * Returns 1 if it can, 0 otherwise.
102022ce4affSfengbojiang  */
102122ce4affSfengbojiang static bool
can_export_rte(struct ucred * td_ucred,bool rt_is_host,const struct sockaddr * rt_dst)102222ce4affSfengbojiang can_export_rte(struct ucred *td_ucred, bool rt_is_host,
102322ce4affSfengbojiang     const struct sockaddr *rt_dst)
102422ce4affSfengbojiang {
102522ce4affSfengbojiang 
102622ce4affSfengbojiang     if ((!rt_is_host) ? jailed_without_vnet(td_ucred)
102722ce4affSfengbojiang         : prison_if(td_ucred, rt_dst) != 0)
102822ce4affSfengbojiang         return (false);
102922ce4affSfengbojiang     return (true);
103022ce4affSfengbojiang }
103122ce4affSfengbojiang 
1032144c6bcdSlogwang int
ff_rtioctl(int fibnum,void * data,unsigned * plen,unsigned maxlen)1033144c6bcdSlogwang ff_rtioctl(int fibnum, void *data, unsigned *plen, unsigned maxlen)
1034144c6bcdSlogwang {
1035144c6bcdSlogwang     struct rt_msghdr *rtm = NULL;
1036144c6bcdSlogwang     struct rtentry *rt = NULL;
103722ce4affSfengbojiang     struct rt_addrinfo info;
103822ce4affSfengbojiang     struct epoch_tracker et;
103922ce4affSfengbojiang #ifdef INET6
104022ce4affSfengbojiang     struct sockaddr_storage ss;
104122ce4affSfengbojiang     struct sockaddr_in6 *sin6;
104222ce4affSfengbojiang     int i, rti_need_deembed = 0;
104322ce4affSfengbojiang #endif
104422ce4affSfengbojiang     int alloc_len = 0, len, error = 0;
104522ce4affSfengbojiang     sa_family_t saf = AF_UNSPEC;
104622ce4affSfengbojiang     struct rib_cmd_info rc;
104722ce4affSfengbojiang     struct nhop_object *nh;
104822ce4affSfengbojiang 
104922ce4affSfengbojiang #define senderr(e) { error = e; goto flush;}
105022ce4affSfengbojiang 
105122ce4affSfengbojiang     len = *plen;
105222ce4affSfengbojiang 
105322ce4affSfengbojiang     /*
105422ce4affSfengbojiang      * Most of current messages are in range 200-240 bytes,
105522ce4affSfengbojiang      * minimize possible re-allocation on reply using larger size
105622ce4affSfengbojiang      * buffer aligned on 1k boundaty.
105722ce4affSfengbojiang      */
105822ce4affSfengbojiang     alloc_len = roundup2(len, 1024);
105922ce4affSfengbojiang     if ((rtm = malloc(alloc_len, M_TEMP, M_NOWAIT)) == NULL)
106022ce4affSfengbojiang         senderr(ENOBUFS);
106122ce4affSfengbojiang 
106222ce4affSfengbojiang     bcopy(data, (caddr_t)rtm, len);
106322ce4affSfengbojiang 
106422ce4affSfengbojiang     if (len < sizeof(*rtm) || len != rtm->rtm_msglen)
106522ce4affSfengbojiang         senderr(EINVAL);
106622ce4affSfengbojiang 
106722ce4affSfengbojiang     bzero(&info, sizeof(info));
106822ce4affSfengbojiang     nh = NULL;
106922ce4affSfengbojiang 
107022ce4affSfengbojiang     if (rtm->rtm_version != RTM_VERSION) {
107122ce4affSfengbojiang         /* Do not touch message since format is unknown */
107222ce4affSfengbojiang         free(rtm, M_TEMP);
107322ce4affSfengbojiang         rtm = NULL;
107422ce4affSfengbojiang         senderr(EPROTONOSUPPORT);
107522ce4affSfengbojiang     }
107622ce4affSfengbojiang 
107722ce4affSfengbojiang     /*
107822ce4affSfengbojiang      * Starting from here, it is possible
107922ce4affSfengbojiang      * to alter original message and insert
108022ce4affSfengbojiang      * caller PID and error value.
108122ce4affSfengbojiang      */
108222ce4affSfengbojiang 
108322ce4affSfengbojiang     if ((error = fill_addrinfo(rtm, len, fibnum, &info)) != 0) {
108422ce4affSfengbojiang         senderr(error);
108522ce4affSfengbojiang     }
108622ce4affSfengbojiang 
108722ce4affSfengbojiang     saf = info.rti_info[RTAX_DST]->sa_family;
108822ce4affSfengbojiang 
108922ce4affSfengbojiang     /* support for new ARP code */
109022ce4affSfengbojiang     if (rtm->rtm_flags & RTF_LLDATA) {
109122ce4affSfengbojiang         error = lla_rt_output(rtm, &info);
109222ce4affSfengbojiang #ifdef INET6
109322ce4affSfengbojiang         if (error == 0)
109422ce4affSfengbojiang             rti_need_deembed = 1;
109522ce4affSfengbojiang #endif
109622ce4affSfengbojiang         goto flush;
109722ce4affSfengbojiang     }
109822ce4affSfengbojiang 
109922ce4affSfengbojiang     union sockaddr_union gw_saun;
110022ce4affSfengbojiang     int blackhole_flags = rtm->rtm_flags & (RTF_BLACKHOLE|RTF_REJECT);
110122ce4affSfengbojiang     if (blackhole_flags != 0) {
110222ce4affSfengbojiang         if (blackhole_flags != (RTF_BLACKHOLE | RTF_REJECT))
110322ce4affSfengbojiang             error = fill_blackholeinfo(&info, &gw_saun);
110422ce4affSfengbojiang         else
110522ce4affSfengbojiang             error = EINVAL;
110622ce4affSfengbojiang         if (error != 0)
110722ce4affSfengbojiang             senderr(error);
110822ce4affSfengbojiang         /* TODO: rebuild rtm from scratch */
110922ce4affSfengbojiang     }
111022ce4affSfengbojiang 
111122ce4affSfengbojiang     switch (rtm->rtm_type) {
111222ce4affSfengbojiang     case RTM_ADD:
111322ce4affSfengbojiang     case RTM_CHANGE:
111422ce4affSfengbojiang         if (rtm->rtm_type == RTM_ADD) {
111522ce4affSfengbojiang             if (info.rti_info[RTAX_GATEWAY] == NULL)
111622ce4affSfengbojiang                 senderr(EINVAL);
111722ce4affSfengbojiang         }
111822ce4affSfengbojiang         error = rib_action(fibnum, rtm->rtm_type, &info, &rc);
111922ce4affSfengbojiang         if (error == 0) {
112022ce4affSfengbojiang #ifdef INET6
112122ce4affSfengbojiang             rti_need_deembed = 1;
112222ce4affSfengbojiang #endif
112322ce4affSfengbojiang #ifdef ROUTE_MPATH
112422ce4affSfengbojiang             if (NH_IS_NHGRP(rc.rc_nh_new) ||
112522ce4affSfengbojiang                 (rc.rc_nh_old && NH_IS_NHGRP(rc.rc_nh_old))) {
112622ce4affSfengbojiang                 struct rib_cmd_info rc_simple = {};
112722ce4affSfengbojiang                 rib_decompose_notification(&rc,
112822ce4affSfengbojiang                     save_add_notification, (void *)&rc_simple);
112922ce4affSfengbojiang                 rc = rc_simple;
113022ce4affSfengbojiang             }
113122ce4affSfengbojiang #endif
113222ce4affSfengbojiang             nh = rc.rc_nh_new;
113322ce4affSfengbojiang             rtm->rtm_index = nh->nh_ifp->if_index;
113422ce4affSfengbojiang             rtm->rtm_flags = rc.rc_rt->rte_flags | nhop_get_rtflags(nh);
113522ce4affSfengbojiang         }
113622ce4affSfengbojiang         break;
113722ce4affSfengbojiang 
113822ce4affSfengbojiang     case RTM_DELETE:
113922ce4affSfengbojiang         error = rib_action(fibnum, RTM_DELETE, &info, &rc);
114022ce4affSfengbojiang         if (error == 0) {
114122ce4affSfengbojiang #ifdef ROUTE_MPATH
114222ce4affSfengbojiang             if (NH_IS_NHGRP(rc.rc_nh_old) ||
114322ce4affSfengbojiang                 (rc.rc_nh_new && NH_IS_NHGRP(rc.rc_nh_new))) {
114422ce4affSfengbojiang                 struct rib_cmd_info rc_simple = {};
114522ce4affSfengbojiang                 rib_decompose_notification(&rc,
114622ce4affSfengbojiang                     save_del_notification, (void *)&rc_simple);
114722ce4affSfengbojiang                 rc = rc_simple;
114822ce4affSfengbojiang             }
114922ce4affSfengbojiang #endif
115022ce4affSfengbojiang             nh = rc.rc_nh_old;
115122ce4affSfengbojiang             goto report;
115222ce4affSfengbojiang         }
115322ce4affSfengbojiang #ifdef INET6
115422ce4affSfengbojiang         /* rt_msg2() will not be used when RTM_DELETE fails. */
115522ce4affSfengbojiang         rti_need_deembed = 1;
115622ce4affSfengbojiang #endif
115722ce4affSfengbojiang         break;
115822ce4affSfengbojiang 
115922ce4affSfengbojiang     case RTM_GET:
116022ce4affSfengbojiang         error = handle_rtm_get(&info, fibnum, rtm, &rc);
116122ce4affSfengbojiang         if (error != 0)
116222ce4affSfengbojiang             senderr(error);
116322ce4affSfengbojiang         nh = rc.rc_nh_new;
116422ce4affSfengbojiang 
116522ce4affSfengbojiang report:
116622ce4affSfengbojiang         if (!can_export_rte(curthread->td_ucred,
116722ce4affSfengbojiang             info.rti_info[RTAX_NETMASK] == NULL,
116822ce4affSfengbojiang             info.rti_info[RTAX_DST])) {
116922ce4affSfengbojiang             senderr(ESRCH);
117022ce4affSfengbojiang         }
117122ce4affSfengbojiang 
117222ce4affSfengbojiang         error = update_rtm_from_rc(&info, &rtm, alloc_len, &rc, nh, maxlen);
117322ce4affSfengbojiang         /*
117422ce4affSfengbojiang          * Note that some sockaddr pointers may have changed to
117522ce4affSfengbojiang          * point to memory outsize @rtm. Some may be pointing
117622ce4affSfengbojiang          * to the on-stack variables.
117722ce4affSfengbojiang          * Given that, any pointer in @info CANNOT BE USED.
117822ce4affSfengbojiang          */
117922ce4affSfengbojiang 
118022ce4affSfengbojiang         /*
118122ce4affSfengbojiang          * scopeid deembedding has been performed while
118222ce4affSfengbojiang          * writing updated rtm in rtsock_msg_buffer().
118322ce4affSfengbojiang          * With that in mind, skip deembedding procedure below.
118422ce4affSfengbojiang          */
118522ce4affSfengbojiang #ifdef INET6
118622ce4affSfengbojiang         rti_need_deembed = 0;
118722ce4affSfengbojiang #endif
118822ce4affSfengbojiang         if (error != 0)
118922ce4affSfengbojiang             senderr(error);
119022ce4affSfengbojiang         break;
119122ce4affSfengbojiang 
119222ce4affSfengbojiang     default:
119322ce4affSfengbojiang         senderr(EOPNOTSUPP);
119422ce4affSfengbojiang     }
119522ce4affSfengbojiang 
119622ce4affSfengbojiang flush:
119722ce4affSfengbojiang     NET_EPOCH_EXIT(et);
119822ce4affSfengbojiang     rt = NULL;
119922ce4affSfengbojiang 
120022ce4affSfengbojiang     if (rtm != NULL) {
120122ce4affSfengbojiang #ifdef INET6
120222ce4affSfengbojiang         if (rti_need_deembed) {
120322ce4affSfengbojiang             /* sin6_scope_id is recovered before sending rtm. */
120422ce4affSfengbojiang             sin6 = (struct sockaddr_in6 *)&ss;
120522ce4affSfengbojiang             for (i = 0; i < RTAX_MAX; i++) {
120622ce4affSfengbojiang                 if (info.rti_info[i] == NULL)
120722ce4affSfengbojiang                     continue;
120822ce4affSfengbojiang                 if (info.rti_info[i]->sa_family != AF_INET6)
120922ce4affSfengbojiang                     continue;
121022ce4affSfengbojiang                 bcopy(info.rti_info[i], sin6, sizeof(*sin6));
121122ce4affSfengbojiang                 if (sa6_recoverscope(sin6) == 0)
121222ce4affSfengbojiang                     bcopy(sin6, info.rti_info[i],
121322ce4affSfengbojiang                             sizeof(*sin6));
121422ce4affSfengbojiang             }
121522ce4affSfengbojiang         }
121622ce4affSfengbojiang #endif
121722ce4affSfengbojiang         if (error != 0)
121822ce4affSfengbojiang             rtm->rtm_errno = error;
121922ce4affSfengbojiang         else
122022ce4affSfengbojiang             rtm->rtm_flags |= RTF_DONE;
122122ce4affSfengbojiang 
122222ce4affSfengbojiang         bcopy((caddr_t)rtm, data, rtm->rtm_msglen);
122322ce4affSfengbojiang         *plen = rtm->rtm_msglen;
122422ce4affSfengbojiang         free(rtm, M_TEMP);
122522ce4affSfengbojiang     }
122622ce4affSfengbojiang 
122722ce4affSfengbojiang     if (error != 0) {
122822ce4affSfengbojiang         ff_os_errno(error);
122922ce4affSfengbojiang         return (-1);
123022ce4affSfengbojiang     }
123122ce4affSfengbojiang 
123222ce4affSfengbojiang     return (error);
123322ce4affSfengbojiang }
123422ce4affSfengbojiang 
123522ce4affSfengbojiang #if 0
123622ce4affSfengbojiang int
123722ce4affSfengbojiang ff_rtioctl_old(int fibnum, void *data, unsigned *plen, unsigned maxlen)
123822ce4affSfengbojiang {
123922ce4affSfengbojiang     struct rt_msghdr *rtm = NULL;
124022ce4affSfengbojiang     struct rtentry *rt = NULL;
1241144c6bcdSlogwang     struct rib_head *rnh;
1242144c6bcdSlogwang     struct rt_addrinfo info;
1243144c6bcdSlogwang     union sockaddr_union saun;
1244144c6bcdSlogwang     sa_family_t saf = AF_UNSPEC;
1245144c6bcdSlogwang     struct sockaddr_storage ss;
1246144c6bcdSlogwang     struct walkarg w;
1247144c6bcdSlogwang     int error = 0, alloc_len = 0, len;
1248144c6bcdSlogwang     struct ifnet *ifp = NULL;
1249144c6bcdSlogwang 
1250144c6bcdSlogwang #ifdef INET6
1251144c6bcdSlogwang     struct sockaddr_in6 *sin6;
1252144c6bcdSlogwang     int i, rti_need_deembed = 0;
1253144c6bcdSlogwang #endif
1254144c6bcdSlogwang 
1255144c6bcdSlogwang #define senderr(e) { error = e; goto flush;}
1256144c6bcdSlogwang 
1257144c6bcdSlogwang     len = *plen;
1258144c6bcdSlogwang     /*
1259144c6bcdSlogwang      * Most of current messages are in range 200-240 bytes,
1260144c6bcdSlogwang      * minimize possible re-allocation on reply using larger size
1261144c6bcdSlogwang      * buffer aligned on 1k boundaty.
1262144c6bcdSlogwang      */
1263144c6bcdSlogwang     alloc_len = roundup2(len, 1024);
1264144c6bcdSlogwang     if ((rtm = malloc(alloc_len, M_TEMP, M_NOWAIT)) == NULL)
1265144c6bcdSlogwang         senderr(ENOBUFS);
1266144c6bcdSlogwang     bcopy(data, (caddr_t)rtm, len);
1267144c6bcdSlogwang 
1268144c6bcdSlogwang     if (len < sizeof(*rtm) || len != rtm->rtm_msglen)
1269144c6bcdSlogwang         senderr(EINVAL);
1270144c6bcdSlogwang 
1271144c6bcdSlogwang     bzero(&info, sizeof(info));
1272144c6bcdSlogwang     bzero(&w, sizeof(w));
1273144c6bcdSlogwang 
1274144c6bcdSlogwang     if (rtm->rtm_version != RTM_VERSION)
1275144c6bcdSlogwang         senderr(EPROTONOSUPPORT);
1276144c6bcdSlogwang 
1277144c6bcdSlogwang     /*
1278144c6bcdSlogwang      * Starting from here, it is possible
1279144c6bcdSlogwang      * to alter original message and insert
1280144c6bcdSlogwang      * caller PID and error value.
1281144c6bcdSlogwang      */
1282144c6bcdSlogwang 
1283144c6bcdSlogwang     rtm->rtm_pid = curproc->p_pid;
1284144c6bcdSlogwang     info.rti_addrs = rtm->rtm_addrs;
1285144c6bcdSlogwang 
1286144c6bcdSlogwang     info.rti_mflags = rtm->rtm_inits;
1287144c6bcdSlogwang     info.rti_rmx = &rtm->rtm_rmx;
1288144c6bcdSlogwang 
1289144c6bcdSlogwang     /*
1290144c6bcdSlogwang      * rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6
1291144c6bcdSlogwang      * link-local address because rtrequest requires addresses with
1292144c6bcdSlogwang      * embedded scope id.
1293144c6bcdSlogwang      */
1294144c6bcdSlogwang     if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info))
1295144c6bcdSlogwang         senderr(EINVAL);
1296144c6bcdSlogwang 
1297144c6bcdSlogwang     info.rti_flags = rtm->rtm_flags;
1298144c6bcdSlogwang     if (info.rti_info[RTAX_DST] == NULL ||
1299144c6bcdSlogwang         info.rti_info[RTAX_DST]->sa_family >= AF_MAX ||
1300144c6bcdSlogwang         (info.rti_info[RTAX_GATEWAY] != NULL &&
1301144c6bcdSlogwang          info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX))
1302144c6bcdSlogwang         senderr(EINVAL);
1303144c6bcdSlogwang     saf = info.rti_info[RTAX_DST]->sa_family;
1304144c6bcdSlogwang 
1305144c6bcdSlogwang     /*
1306144c6bcdSlogwang      * The given gateway address may be an interface address.
1307144c6bcdSlogwang      * For example, issuing a "route change" command on a route
1308144c6bcdSlogwang      * entry that was created from a tunnel, and the gateway
1309144c6bcdSlogwang      * address given is the local end point. In this case the
1310144c6bcdSlogwang      * RTF_GATEWAY flag must be cleared or the destination will
1311144c6bcdSlogwang      * not be reachable even though there is no error message.
1312144c6bcdSlogwang      */
1313144c6bcdSlogwang     if (info.rti_info[RTAX_GATEWAY] != NULL &&
1314144c6bcdSlogwang         info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
1315144c6bcdSlogwang         struct rt_addrinfo ginfo;
1316144c6bcdSlogwang         struct sockaddr *gdst;
1317144c6bcdSlogwang 
1318144c6bcdSlogwang         bzero(&ginfo, sizeof(ginfo));
1319144c6bcdSlogwang         bzero(&ss, sizeof(ss));
1320144c6bcdSlogwang         ss.ss_len = sizeof(ss);
1321144c6bcdSlogwang 
1322144c6bcdSlogwang         ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
1323144c6bcdSlogwang         gdst = info.rti_info[RTAX_GATEWAY];
1324144c6bcdSlogwang 
1325144c6bcdSlogwang         /*
1326144c6bcdSlogwang          * A host route through the loopback interface is
1327144c6bcdSlogwang          * installed for each interface adddress. In pre 8.0
1328144c6bcdSlogwang          * releases the interface address of a PPP link type
1329144c6bcdSlogwang          * is not reachable locally. This behavior is fixed as
1330144c6bcdSlogwang          * part of the new L2/L3 redesign and rewrite work. The
1331144c6bcdSlogwang          * signature of this interface address route is the
1332144c6bcdSlogwang          * AF_LINK sa_family type of the rt_gateway, and the
1333144c6bcdSlogwang          * rt_ifp has the IFF_LOOPBACK flag set.
1334144c6bcdSlogwang          */
1335144c6bcdSlogwang         if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
1336144c6bcdSlogwang             if (ss.ss_family == AF_LINK &&
1337144c6bcdSlogwang                 ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
1338144c6bcdSlogwang                 info.rti_flags &= ~RTF_GATEWAY;
1339144c6bcdSlogwang                 info.rti_flags |= RTF_GWFLAG_COMPAT;
1340144c6bcdSlogwang             }
1341144c6bcdSlogwang             rib_free_info(&ginfo);
1342144c6bcdSlogwang         }
1343144c6bcdSlogwang     }
1344144c6bcdSlogwang 
1345144c6bcdSlogwang     switch (rtm->rtm_type) {
1346144c6bcdSlogwang         struct rtentry *saved_nrt;
1347144c6bcdSlogwang 
1348144c6bcdSlogwang     case RTM_ADD:
1349144c6bcdSlogwang     case RTM_CHANGE:
1350144c6bcdSlogwang         if (info.rti_info[RTAX_GATEWAY] == NULL)
1351144c6bcdSlogwang             senderr(EINVAL);
1352144c6bcdSlogwang         saved_nrt = NULL;
1353144c6bcdSlogwang 
1354144c6bcdSlogwang         /* support for new ARP code */
1355144c6bcdSlogwang         if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK &&
1356144c6bcdSlogwang             (rtm->rtm_flags & RTF_LLDATA) != 0) {
1357144c6bcdSlogwang             error = lla_rt_output(rtm, &info);
1358144c6bcdSlogwang #ifdef INET6
1359144c6bcdSlogwang             if (error == 0)
1360144c6bcdSlogwang                 rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
1361144c6bcdSlogwang #endif
1362144c6bcdSlogwang             break;
1363144c6bcdSlogwang         }
1364144c6bcdSlogwang         error = rtrequest1_fib(rtm->rtm_type, &info, &saved_nrt,
1365144c6bcdSlogwang             fibnum);
1366144c6bcdSlogwang         if (error == 0 && saved_nrt != NULL) {
1367144c6bcdSlogwang #ifdef INET6
1368144c6bcdSlogwang             rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
1369144c6bcdSlogwang #endif
1370144c6bcdSlogwang             RT_LOCK(saved_nrt);
1371144c6bcdSlogwang             rtm->rtm_index = saved_nrt->rt_ifp->if_index;
1372144c6bcdSlogwang             RT_REMREF(saved_nrt);
1373144c6bcdSlogwang             RT_UNLOCK(saved_nrt);
1374144c6bcdSlogwang         }
1375144c6bcdSlogwang         break;
1376144c6bcdSlogwang 
1377144c6bcdSlogwang     case RTM_DELETE:
1378144c6bcdSlogwang         saved_nrt = NULL;
1379144c6bcdSlogwang         /* support for new ARP code */
1380144c6bcdSlogwang         if (info.rti_info[RTAX_GATEWAY] &&
1381144c6bcdSlogwang             (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
1382144c6bcdSlogwang             (rtm->rtm_flags & RTF_LLDATA) != 0) {
1383144c6bcdSlogwang             error = lla_rt_output(rtm, &info);
1384144c6bcdSlogwang #ifdef INET6
1385144c6bcdSlogwang             if (error == 0)
1386144c6bcdSlogwang                 rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
1387144c6bcdSlogwang #endif
1388144c6bcdSlogwang             break;
1389144c6bcdSlogwang         }
1390144c6bcdSlogwang         error = rtrequest1_fib(RTM_DELETE, &info, &saved_nrt, fibnum);
1391144c6bcdSlogwang         if (error == 0) {
1392144c6bcdSlogwang             RT_LOCK(saved_nrt);
1393144c6bcdSlogwang             rt = saved_nrt;
1394144c6bcdSlogwang             goto report;
1395144c6bcdSlogwang         }
1396144c6bcdSlogwang #ifdef INET6
1397144c6bcdSlogwang         /* rt_msg2() will not be used when RTM_DELETE fails. */
1398144c6bcdSlogwang         rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
1399144c6bcdSlogwang #endif
1400144c6bcdSlogwang         break;
1401144c6bcdSlogwang 
1402144c6bcdSlogwang     case RTM_GET:
1403144c6bcdSlogwang         rnh = rt_tables_get_rnh(fibnum, saf);
1404144c6bcdSlogwang         if (rnh == NULL)
1405144c6bcdSlogwang             senderr(EAFNOSUPPORT);
1406144c6bcdSlogwang 
1407144c6bcdSlogwang         RIB_RLOCK(rnh);
1408144c6bcdSlogwang 
1409144c6bcdSlogwang         if (info.rti_info[RTAX_NETMASK] == NULL &&
1410144c6bcdSlogwang             rtm->rtm_type == RTM_GET) {
1411144c6bcdSlogwang             /*
1412144c6bcdSlogwang              * Provide logest prefix match for
1413144c6bcdSlogwang              * address lookup (no mask).
1414144c6bcdSlogwang              * 'route -n get addr'
1415144c6bcdSlogwang              */
1416144c6bcdSlogwang             rt = (struct rtentry *) rnh->rnh_matchaddr(
1417144c6bcdSlogwang                 info.rti_info[RTAX_DST], &rnh->head);
1418144c6bcdSlogwang         } else
1419144c6bcdSlogwang             rt = (struct rtentry *) rnh->rnh_lookup(
1420144c6bcdSlogwang                 info.rti_info[RTAX_DST],
1421144c6bcdSlogwang                 info.rti_info[RTAX_NETMASK], &rnh->head);
1422144c6bcdSlogwang 
1423144c6bcdSlogwang         if (rt == NULL) {
1424144c6bcdSlogwang             RIB_RUNLOCK(rnh);
1425144c6bcdSlogwang             senderr(ESRCH);
1426144c6bcdSlogwang         }
1427144c6bcdSlogwang #ifdef RADIX_MPATH
1428144c6bcdSlogwang         /*
1429144c6bcdSlogwang          * for RTM_CHANGE/LOCK, if we got multipath routes,
1430144c6bcdSlogwang          * we require users to specify a matching RTAX_GATEWAY.
1431144c6bcdSlogwang          *
1432144c6bcdSlogwang          * for RTM_GET, gate is optional even with multipath.
1433144c6bcdSlogwang          * if gate == NULL the first match is returned.
1434144c6bcdSlogwang          * (no need to call rt_mpath_matchgate if gate == NULL)
1435144c6bcdSlogwang          */
1436144c6bcdSlogwang         if (rt_mpath_capable(rnh) &&
1437144c6bcdSlogwang             (rtm->rtm_type != RTM_GET || info.rti_info[RTAX_GATEWAY])) {
1438144c6bcdSlogwang             rt = rt_mpath_matchgate(rt, info.rti_info[RTAX_GATEWAY]);
1439144c6bcdSlogwang             if (!rt) {
1440144c6bcdSlogwang                 RIB_RUNLOCK(rnh);
1441144c6bcdSlogwang                 senderr(ESRCH);
1442144c6bcdSlogwang             }
1443144c6bcdSlogwang         }
1444144c6bcdSlogwang #endif
1445144c6bcdSlogwang         /*
1446144c6bcdSlogwang          * If performing proxied L2 entry insertion, and
1447144c6bcdSlogwang          * the actual PPP host entry is found, perform
1448144c6bcdSlogwang          * another search to retrieve the prefix route of
1449144c6bcdSlogwang          * the local end point of the PPP link.
1450144c6bcdSlogwang          */
1451144c6bcdSlogwang         if (rtm->rtm_flags & RTF_ANNOUNCE) {
1452144c6bcdSlogwang             struct sockaddr laddr;
1453144c6bcdSlogwang 
1454144c6bcdSlogwang             if (rt->rt_ifp != NULL &&
1455144c6bcdSlogwang                 rt->rt_ifp->if_type == IFT_PROPVIRTUAL) {
1456144c6bcdSlogwang                 struct ifaddr *ifa;
1457144c6bcdSlogwang 
1458144c6bcdSlogwang                 ifa = ifa_ifwithnet(info.rti_info[RTAX_DST], 1,
1459144c6bcdSlogwang                         RT_ALL_FIBS);
1460144c6bcdSlogwang                 if (ifa != NULL)
1461144c6bcdSlogwang                     rt_maskedcopy(ifa->ifa_addr,
1462144c6bcdSlogwang                               &laddr,
1463144c6bcdSlogwang                               ifa->ifa_netmask);
1464144c6bcdSlogwang             } else
1465144c6bcdSlogwang                 rt_maskedcopy(rt->rt_ifa->ifa_addr,
1466144c6bcdSlogwang                           &laddr,
1467144c6bcdSlogwang                           rt->rt_ifa->ifa_netmask);
1468144c6bcdSlogwang             /*
1469144c6bcdSlogwang              * refactor rt and no lock operation necessary
1470144c6bcdSlogwang              */
1471144c6bcdSlogwang             rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr,
1472144c6bcdSlogwang                 &rnh->head);
1473144c6bcdSlogwang             if (rt == NULL) {
1474144c6bcdSlogwang                 RIB_RUNLOCK(rnh);
1475144c6bcdSlogwang                 senderr(ESRCH);
1476144c6bcdSlogwang             }
1477144c6bcdSlogwang         }
1478144c6bcdSlogwang         RT_LOCK(rt);
1479144c6bcdSlogwang         RT_ADDREF(rt);
1480144c6bcdSlogwang         RIB_RUNLOCK(rnh);
1481144c6bcdSlogwang 
1482144c6bcdSlogwang report:
1483144c6bcdSlogwang         RT_LOCK_ASSERT(rt);
1484144c6bcdSlogwang         if ((rt->rt_flags & RTF_HOST) == 0
1485144c6bcdSlogwang             ? jailed_without_vnet(curthread->td_ucred)
1486144c6bcdSlogwang             : prison_if(curthread->td_ucred,
1487144c6bcdSlogwang             rt_key(rt)) != 0) {
1488144c6bcdSlogwang             RT_UNLOCK(rt);
1489144c6bcdSlogwang             senderr(ESRCH);
1490144c6bcdSlogwang         }
1491144c6bcdSlogwang         info.rti_info[RTAX_DST] = rt_key(rt);
1492144c6bcdSlogwang         info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
1493144c6bcdSlogwang         info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt),
1494144c6bcdSlogwang             rt_mask(rt), &ss);
1495144c6bcdSlogwang         info.rti_info[RTAX_GENMASK] = 0;
1496144c6bcdSlogwang         if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
1497144c6bcdSlogwang             ifp = rt->rt_ifp;
1498144c6bcdSlogwang             if (ifp) {
1499144c6bcdSlogwang                 info.rti_info[RTAX_IFP] =
1500144c6bcdSlogwang                     ifp->if_addr->ifa_addr;
1501144c6bcdSlogwang                 error = rtm_get_jailed(&info, ifp, rt,
1502144c6bcdSlogwang                     &saun, curthread->td_ucred);
1503144c6bcdSlogwang                 if (error != 0) {
1504144c6bcdSlogwang                     RT_UNLOCK(rt);
1505144c6bcdSlogwang                     senderr(error);
1506144c6bcdSlogwang                 }
1507144c6bcdSlogwang                 if (ifp->if_flags & IFF_POINTOPOINT)
1508144c6bcdSlogwang                     info.rti_info[RTAX_BRD] =
1509144c6bcdSlogwang                         rt->rt_ifa->ifa_dstaddr;
1510144c6bcdSlogwang                 rtm->rtm_index = ifp->if_index;
1511144c6bcdSlogwang             } else {
1512144c6bcdSlogwang                 info.rti_info[RTAX_IFP] = NULL;
1513144c6bcdSlogwang                 info.rti_info[RTAX_IFA] = NULL;
1514144c6bcdSlogwang             }
1515144c6bcdSlogwang         } else if ((ifp = rt->rt_ifp) != NULL) {
1516144c6bcdSlogwang             rtm->rtm_index = ifp->if_index;
1517144c6bcdSlogwang         }
1518144c6bcdSlogwang 
1519144c6bcdSlogwang         /* Check if we need to realloc storage */
1520144c6bcdSlogwang         rtsock_msg_buffer(rtm->rtm_type, &info, NULL, &len);
1521144c6bcdSlogwang         if (len > maxlen) {
1522144c6bcdSlogwang             RT_UNLOCK(rt);
1523144c6bcdSlogwang             senderr(ENOBUFS);
1524144c6bcdSlogwang         }
1525144c6bcdSlogwang 
1526144c6bcdSlogwang         if (len > alloc_len) {
1527144c6bcdSlogwang             struct rt_msghdr *new_rtm;
1528144c6bcdSlogwang             new_rtm = malloc(len, M_TEMP, M_NOWAIT);
1529144c6bcdSlogwang             if (new_rtm == NULL) {
1530144c6bcdSlogwang                 RT_UNLOCK(rt);
1531144c6bcdSlogwang                 senderr(ENOBUFS);
1532144c6bcdSlogwang             }
1533144c6bcdSlogwang             bcopy(rtm, new_rtm, rtm->rtm_msglen);
1534144c6bcdSlogwang             free(rtm, M_TEMP);
1535144c6bcdSlogwang             rtm = new_rtm;
1536144c6bcdSlogwang             alloc_len = len;
1537144c6bcdSlogwang         }
1538144c6bcdSlogwang 
1539144c6bcdSlogwang         w.w_tmem = (caddr_t)rtm;
1540144c6bcdSlogwang         w.w_tmemsize = alloc_len;
1541144c6bcdSlogwang         rtsock_msg_buffer(rtm->rtm_type, &info, &w, &len);
1542144c6bcdSlogwang 
1543144c6bcdSlogwang         if (rt->rt_flags & RTF_GWFLAG_COMPAT)
1544144c6bcdSlogwang             rtm->rtm_flags = RTF_GATEWAY |
1545144c6bcdSlogwang                 (rt->rt_flags & ~RTF_GWFLAG_COMPAT);
1546144c6bcdSlogwang         else
1547144c6bcdSlogwang             rtm->rtm_flags = rt->rt_flags;
1548144c6bcdSlogwang         rt_getmetrics(rt, &rtm->rtm_rmx);
1549144c6bcdSlogwang         rtm->rtm_addrs = info.rti_addrs;
1550144c6bcdSlogwang 
1551144c6bcdSlogwang         RT_UNLOCK(rt);
1552144c6bcdSlogwang         break;
1553144c6bcdSlogwang 
1554144c6bcdSlogwang     default:
1555144c6bcdSlogwang         senderr(EOPNOTSUPP);
1556144c6bcdSlogwang     }
1557144c6bcdSlogwang 
1558144c6bcdSlogwang flush:
1559144c6bcdSlogwang     if (rt != NULL)
1560144c6bcdSlogwang         RTFREE(rt);
1561144c6bcdSlogwang 
1562144c6bcdSlogwang     if (rtm != NULL) {
1563144c6bcdSlogwang #ifdef INET6
1564144c6bcdSlogwang         if (rti_need_deembed) {
1565144c6bcdSlogwang             /* sin6_scope_id is recovered before sending rtm. */
1566144c6bcdSlogwang             sin6 = (struct sockaddr_in6 *)&ss;
1567144c6bcdSlogwang             for (i = 0; i < RTAX_MAX; i++) {
1568144c6bcdSlogwang                 if (info.rti_info[i] == NULL)
1569144c6bcdSlogwang                     continue;
1570144c6bcdSlogwang                 if (info.rti_info[i]->sa_family != AF_INET6)
1571144c6bcdSlogwang                     continue;
1572144c6bcdSlogwang                 bcopy(info.rti_info[i], sin6, sizeof(*sin6));
1573144c6bcdSlogwang                 if (sa6_recoverscope(sin6) == 0)
1574144c6bcdSlogwang                     bcopy(sin6, info.rti_info[i],
1575144c6bcdSlogwang                             sizeof(*sin6));
1576144c6bcdSlogwang             }
1577144c6bcdSlogwang         }
1578144c6bcdSlogwang #endif
1579144c6bcdSlogwang         if (error != 0)
1580144c6bcdSlogwang             rtm->rtm_errno = error;
1581144c6bcdSlogwang         else
1582144c6bcdSlogwang             rtm->rtm_flags |= RTF_DONE;
1583144c6bcdSlogwang 
1584144c6bcdSlogwang         bcopy((caddr_t)rtm, data, rtm->rtm_msglen);
1585144c6bcdSlogwang         *plen = rtm->rtm_msglen;
1586144c6bcdSlogwang         free(rtm, M_TEMP);
1587144c6bcdSlogwang     }
1588144c6bcdSlogwang 
15893b2bd0f6Slogwang     if (error != 0) {
15903b2bd0f6Slogwang         ff_os_errno(error);
15913b2bd0f6Slogwang         return (-1);
15923b2bd0f6Slogwang     }
15933b2bd0f6Slogwang 
1594144c6bcdSlogwang     return (error);
1595144c6bcdSlogwang }
159622ce4affSfengbojiang #endif
1597