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