Split gigantic rtsock route_output() into smaller functions.

Amount of changes to the original code has been intentionally minimised
to ease diffing.
The changes are mostly mechanical, with the following exceptions:

* lltable handler is now called directly based of RTF_LLINFO flag presense.
* "report" logic for updating rtm in RTM_GET/RTM_DELETE has been simplified,
  fixing several potential use-after-free cases in rt_addrinfo.
* llable asserts has been replaced with error-returning, preventing kernel
  crashes when lltable gw af family is invalid (root required).

MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D22864
This commit is contained in:
Alexander V. Chernikov 2019-12-31 17:26:53 +00:00
parent e11c9783e1
commit c83dda362e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=356236
5 changed files with 379 additions and 265 deletions

View File

@ -2782,13 +2782,13 @@ prison_check_af(struct ucred *cred, int af)
* the jail doesn't allow the address family. IPv4 Address passed in in NBO.
*/
int
prison_if(struct ucred *cred, struct sockaddr *sa)
prison_if(struct ucred *cred, const struct sockaddr *sa)
{
#ifdef INET
struct sockaddr_in *sai;
const struct sockaddr_in *sai;
#endif
#ifdef INET6
struct sockaddr_in6 *sai6;
const struct sockaddr_in6 *sai6;
#endif
int error;
@ -2805,13 +2805,13 @@ prison_if(struct ucred *cred, struct sockaddr *sa)
{
#ifdef INET
case AF_INET:
sai = (struct sockaddr_in *)sa;
sai = (const struct sockaddr_in *)sa;
error = prison_check_ip4(cred, &sai->sin_addr);
break;
#endif
#ifdef INET6
case AF_INET6:
sai6 = (struct sockaddr_in6 *)sa;
sai6 = (const struct sockaddr_in6 *)sa;
error = prison_check_ip6(cred, &sai6->sin6_addr);
break;
#endif

View File

@ -663,8 +663,8 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
u_int laflags = 0;
int error;
KASSERT(dl != NULL && dl->sdl_family == AF_LINK,
("%s: invalid dl\n", __func__));
if (dl == NULL || dl->sdl_family != AF_LINK)
return (EINVAL);
ifp = ifnet_byindex(dl->sdl_index);
if (ifp == NULL) {
@ -681,7 +681,8 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
break;
}
LLTABLE_LIST_RUNLOCK();
KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n"));
if (llt == NULL)
return (ESRCH);
error = 0;

View File

@ -145,6 +145,8 @@ struct rtentry {
*/
#define rt_key(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_key)))
#define rt_mask(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_mask)))
#define rt_key_const(r) (*((const struct sockaddr * const *)(&(r)->rt_nodes->rn_key)))
#define rt_mask_const(r) (*((const struct sockaddr * const *)(&(r)->rt_nodes->rn_mask)))
struct sockaddr *rt_gateway; /* value */
struct ifnet *rt_ifp; /* the answer: interface to use */
struct ifaddr *rt_ifa; /* the answer: interface address to use */

View File

@ -181,6 +181,15 @@ static void rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out);
static void rt_dispatch(struct mbuf *, sa_family_t);
static struct sockaddr *rtsock_fix_netmask(struct sockaddr *dst,
struct sockaddr *smask, struct sockaddr_storage *dmask);
static int handle_rtm_get(struct rt_addrinfo *info, u_int fibnum,
struct rt_msghdr *rtm, struct rtentry **ret_nrt);
static int update_rtm_from_rte(struct rt_addrinfo *info,
struct rt_msghdr **prtm, int alloc_len,
struct rtentry *rt);
static void send_rtm_reply(struct socket *so, struct rt_msghdr *rtm,
struct mbuf *m, sa_family_t saf, u_int fibnum,
int rtm_errno);
static int can_export_rte(struct ucred *td_ucred, const struct rtentry *rt);
static struct netisr_handler rtsock_nh = {
.nh_name = "rtsock",
@ -550,14 +559,287 @@ rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp,
return (0);
}
/*
* Fills in @info based on userland-provided @rtm message.
*
* Returns 0 on success.
*/
static int
fill_addrinfo(struct rt_msghdr *rtm, int len, u_int fibnum, struct rt_addrinfo *info)
{
int error;
sa_family_t saf;
rtm->rtm_pid = curproc->p_pid;
info->rti_addrs = rtm->rtm_addrs;
info->rti_mflags = rtm->rtm_inits;
info->rti_rmx = &rtm->rtm_rmx;
/*
* rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6
* link-local address because rtrequest requires addresses with
* embedded scope id.
*/
if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, info))
return (EINVAL);
if (rtm->rtm_flags & RTF_RNH_LOCKED)
return (EINVAL);
info->rti_flags = rtm->rtm_flags;
if (info->rti_info[RTAX_DST] == NULL ||
info->rti_info[RTAX_DST]->sa_family >= AF_MAX ||
(info->rti_info[RTAX_GATEWAY] != NULL &&
info->rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX))
return (EINVAL);
saf = info->rti_info[RTAX_DST]->sa_family;
/*
* Verify that the caller has the appropriate privilege; RTM_GET
* is the only operation the non-superuser is allowed.
*/
if (rtm->rtm_type != RTM_GET) {
error = priv_check(curthread, PRIV_NET_ROUTE);
if (error != 0)
return (error);
}
/*
* The given gateway address may be an interface address.
* For example, issuing a "route change" command on a route
* entry that was created from a tunnel, and the gateway
* address given is the local end point. In this case the
* RTF_GATEWAY flag must be cleared or the destination will
* not be reachable even though there is no error message.
*/
if (info->rti_info[RTAX_GATEWAY] != NULL &&
info->rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
struct rt_addrinfo ginfo;
struct sockaddr *gdst;
struct sockaddr_storage ss;
bzero(&ginfo, sizeof(ginfo));
bzero(&ss, sizeof(ss));
ss.ss_len = sizeof(ss);
ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
gdst = info->rti_info[RTAX_GATEWAY];
/*
* A host route through the loopback interface is
* installed for each interface adddress. In pre 8.0
* releases the interface address of a PPP link type
* is not reachable locally. This behavior is fixed as
* part of the new L2/L3 redesign and rewrite work. The
* signature of this interface address route is the
* AF_LINK sa_family type of the rt_gateway, and the
* rt_ifp has the IFF_LOOPBACK flag set.
*/
if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
if (ss.ss_family == AF_LINK &&
ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
info->rti_flags &= ~RTF_GATEWAY;
info->rti_flags |= RTF_GWFLAG_COMPAT;
}
rib_free_info(&ginfo);
}
}
return (0);
}
/*
* Handles RTM_GET message from routing socket, returning matching rt.
*
* Returns:
* 0 on success, with locked and referenced matching rt in @rt_nrt
* errno of failure
*/
static int
handle_rtm_get(struct rt_addrinfo *info, u_int fibnum,
struct rt_msghdr *rtm, struct rtentry **ret_nrt)
{
RIB_RLOCK_TRACKER;
struct rtentry *rt;
struct rib_head *rnh;
sa_family_t saf;
saf = info->rti_info[RTAX_DST]->sa_family;
rnh = rt_tables_get_rnh(fibnum, saf);
if (rnh == NULL)
return (EAFNOSUPPORT);
RIB_RLOCK(rnh);
if (info->rti_info[RTAX_NETMASK] == NULL) {
/*
* Provide longest prefix match for
* address lookup (no mask).
* 'route -n get addr'
*/
rt = (struct rtentry *) rnh->rnh_matchaddr(
info->rti_info[RTAX_DST], &rnh->head);
} else
rt = (struct rtentry *) rnh->rnh_lookup(
info->rti_info[RTAX_DST],
info->rti_info[RTAX_NETMASK], &rnh->head);
if (rt == NULL) {
RIB_RUNLOCK(rnh);
return (ESRCH);
}
#ifdef RADIX_MPATH
/*
* for RTM_GET, gate is optional even with multipath.
* if gate == NULL the first match is returned.
* (no need to call rt_mpath_matchgate if gate == NULL)
*/
if (rt_mpath_capable(rnh) && info->rti_info[RTAX_GATEWAY]) {
rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]);
if (!rt) {
RIB_RUNLOCK(rnh);
return (ESRCH);
}
}
#endif
/*
* If performing proxied L2 entry insertion, and
* the actual PPP host entry is found, perform
* another search to retrieve the prefix route of
* the local end point of the PPP link.
*/
if (rtm->rtm_flags & RTF_ANNOUNCE) {
struct sockaddr laddr;
if (rt->rt_ifp != NULL &&
rt->rt_ifp->if_type == IFT_PROPVIRTUAL) {
struct epoch_tracker et;
struct ifaddr *ifa;
NET_EPOCH_ENTER(et);
ifa = ifa_ifwithnet(info->rti_info[RTAX_DST], 1,
RT_ALL_FIBS);
NET_EPOCH_EXIT(et);
if (ifa != NULL)
rt_maskedcopy(ifa->ifa_addr,
&laddr,
ifa->ifa_netmask);
} else
rt_maskedcopy(rt->rt_ifa->ifa_addr,
&laddr,
rt->rt_ifa->ifa_netmask);
/*
* refactor rt and no lock operation necessary
*/
rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr,
&rnh->head);
if (rt == NULL) {
RIB_RUNLOCK(rnh);
return (ESRCH);
}
}
RT_LOCK(rt);
RT_ADDREF(rt);
RIB_RUNLOCK(rnh);
*ret_nrt = rt;
return (0);
}
/*
* Update sockaddrs, flags, etc in @prtm based on @rt data.
* Assumes @rt is locked.
* rtm can be reallocated.
*
* Returns 0 on success, along with pointer to (potentially reallocated)
* rtm.
*
*/
static int
update_rtm_from_rte(struct rt_addrinfo *info, struct rt_msghdr **prtm,
int alloc_len, struct rtentry *rt)
{
struct sockaddr_storage netmask_ss;
struct walkarg w;
union sockaddr_union saun;
struct rt_msghdr *rtm, *orig_rtm = NULL;
struct ifnet *ifp;
int error, len;
RT_LOCK_ASSERT(rt);
rtm = *prtm;
info->rti_info[RTAX_DST] = rt_key(rt);
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info->rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt),
rt_mask(rt), &netmask_ss);
info->rti_info[RTAX_GENMASK] = 0;
ifp = rt->rt_ifp;
if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
if (ifp) {
info->rti_info[RTAX_IFP] =
ifp->if_addr->ifa_addr;
error = rtm_get_jailed(info, ifp, rt,
&saun, curthread->td_ucred);
if (error != 0)
return (error);
if (ifp->if_flags & IFF_POINTOPOINT)
info->rti_info[RTAX_BRD] =
rt->rt_ifa->ifa_dstaddr;
rtm->rtm_index = ifp->if_index;
} else {
info->rti_info[RTAX_IFP] = NULL;
info->rti_info[RTAX_IFA] = NULL;
}
} else if (ifp != NULL)
rtm->rtm_index = ifp->if_index;
/* Check if we need to realloc storage */
rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len);
if (len > alloc_len) {
struct rt_msghdr *tmp_rtm;
tmp_rtm = malloc(len, M_TEMP, M_NOWAIT);
if (tmp_rtm == NULL)
return (ENOBUFS);
bcopy(rtm, tmp_rtm, rtm->rtm_msglen);
orig_rtm = rtm;
rtm = tmp_rtm;
alloc_len = len;
/*
* Delay freeing original rtm as info contains
* data referencing it.
*/
}
w.w_tmem = (caddr_t)rtm;
w.w_tmemsize = alloc_len;
rtsock_msg_buffer(rtm->rtm_type, info, &w, &len);
if (rt->rt_flags & RTF_GWFLAG_COMPAT)
rtm->rtm_flags = RTF_GATEWAY |
(rt->rt_flags & ~RTF_GWFLAG_COMPAT);
else
rtm->rtm_flags = rt->rt_flags;
rt_getmetrics(rt, &rtm->rtm_rmx);
rtm->rtm_addrs = info->rti_addrs;
if (orig_rtm != NULL)
free(orig_rtm, M_TEMP);
*prtm = rtm;
return (0);
}
/*ARGSUSED*/
static int
route_output(struct mbuf *m, struct socket *so, ...)
{
RIB_RLOCK_TRACKER;
struct rt_msghdr *rtm = NULL;
struct rtentry *rt = NULL;
struct rib_head *rnh;
struct rt_addrinfo info;
struct sockaddr_storage ss;
struct epoch_tracker et;
@ -566,10 +848,7 @@ route_output(struct mbuf *m, struct socket *so, ...)
int i, rti_need_deembed = 0;
#endif
int alloc_len = 0, len, error = 0, fibnum;
struct ifnet *ifp = NULL;
union sockaddr_union saun;
sa_family_t saf = AF_UNSPEC;
struct rawcb *rp = NULL;
struct walkarg w;
fibnum = so->so_fibnum;
@ -612,77 +891,20 @@ route_output(struct mbuf *m, struct socket *so, ...)
* caller PID and error value.
*/
rtm->rtm_pid = curproc->p_pid;
info.rti_addrs = rtm->rtm_addrs;
info.rti_mflags = rtm->rtm_inits;
info.rti_rmx = &rtm->rtm_rmx;
/*
* rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6
* link-local address because rtrequest requires addresses with
* embedded scope id.
*/
if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, &info))
senderr(EINVAL);
if (rtm->rtm_flags & RTF_RNH_LOCKED)
senderr(EINVAL);
info.rti_flags = rtm->rtm_flags;
if (info.rti_info[RTAX_DST] == NULL ||
info.rti_info[RTAX_DST]->sa_family >= AF_MAX ||
(info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX))
senderr(EINVAL);
saf = info.rti_info[RTAX_DST]->sa_family;
/*
* Verify that the caller has the appropriate privilege; RTM_GET
* is the only operation the non-superuser is allowed.
*/
if (rtm->rtm_type != RTM_GET) {
error = priv_check(curthread, PRIV_NET_ROUTE);
if (error)
senderr(error);
if ((error = fill_addrinfo(rtm, len, fibnum, &info)) != 0) {
senderr(error);
}
/*
* The given gateway address may be an interface address.
* For example, issuing a "route change" command on a route
* entry that was created from a tunnel, and the gateway
* address given is the local end point. In this case the
* RTF_GATEWAY flag must be cleared or the destination will
* not be reachable even though there is no error message.
*/
if (info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) {
struct rt_addrinfo ginfo;
struct sockaddr *gdst;
saf = info.rti_info[RTAX_DST]->sa_family;
bzero(&ginfo, sizeof(ginfo));
bzero(&ss, sizeof(ss));
ss.ss_len = sizeof(ss);
ginfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ss;
gdst = info.rti_info[RTAX_GATEWAY];
/*
* A host route through the loopback interface is
* installed for each interface adddress. In pre 8.0
* releases the interface address of a PPP link type
* is not reachable locally. This behavior is fixed as
* part of the new L2/L3 redesign and rewrite work. The
* signature of this interface address route is the
* AF_LINK sa_family type of the rt_gateway, and the
* rt_ifp has the IFF_LOOPBACK flag set.
*/
if (rib_lookup_info(fibnum, gdst, NHR_REF, 0, &ginfo) == 0) {
if (ss.ss_family == AF_LINK &&
ginfo.rti_ifp->if_flags & IFF_LOOPBACK) {
info.rti_flags &= ~RTF_GATEWAY;
info.rti_flags |= RTF_GWFLAG_COMPAT;
}
rib_free_info(&ginfo);
}
/* support for new ARP code */
if (rtm->rtm_flags & RTF_LLDATA) {
error = lla_rt_output(rtm, &info);
#ifdef INET6
if (error == 0)
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
#endif
goto flush;
}
switch (rtm->rtm_type) {
@ -695,18 +917,6 @@ route_output(struct mbuf *m, struct socket *so, ...)
senderr(EINVAL);
}
saved_nrt = NULL;
/* support for new ARP code */
if (info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK &&
(rtm->rtm_flags & RTF_LLDATA) != 0) {
error = lla_rt_output(rtm, &info);
#ifdef INET6
if (error == 0)
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
#endif
break;
}
error = rtrequest1_fib(rtm->rtm_type, &info, &saved_nrt,
fibnum);
if (error == 0 && saved_nrt != NULL) {
@ -722,17 +932,6 @@ route_output(struct mbuf *m, struct socket *so, ...)
case RTM_DELETE:
saved_nrt = NULL;
/* support for new ARP code */
if (info.rti_info[RTAX_GATEWAY] &&
(info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
(rtm->rtm_flags & RTF_LLDATA) != 0) {
error = lla_rt_output(rtm, &info);
#ifdef INET6
if (error == 0)
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0;
#endif
break;
}
error = rtrequest1_fib(RTM_DELETE, &info, &saved_nrt, fibnum);
if (error == 0) {
RT_LOCK(saved_nrt);
@ -746,153 +945,33 @@ route_output(struct mbuf *m, struct socket *so, ...)
break;
case RTM_GET:
rnh = rt_tables_get_rnh(fibnum, saf);
if (rnh == NULL)
senderr(EAFNOSUPPORT);
RIB_RLOCK(rnh);
if (info.rti_info[RTAX_NETMASK] == NULL &&
rtm->rtm_type == RTM_GET) {
/*
* Provide longest prefix match for
* address lookup (no mask).
* 'route -n get addr'
*/
rt = (struct rtentry *) rnh->rnh_matchaddr(
info.rti_info[RTAX_DST], &rnh->head);
} else
rt = (struct rtentry *) rnh->rnh_lookup(
info.rti_info[RTAX_DST],
info.rti_info[RTAX_NETMASK], &rnh->head);
if (rt == NULL) {
RIB_RUNLOCK(rnh);
senderr(ESRCH);
}
#ifdef RADIX_MPATH
/*
* for RTM_CHANGE/LOCK, if we got multipath routes,
* we require users to specify a matching RTAX_GATEWAY.
*
* for RTM_GET, gate is optional even with multipath.
* if gate == NULL the first match is returned.
* (no need to call rt_mpath_matchgate if gate == NULL)
*/
if (rt_mpath_capable(rnh) &&
(rtm->rtm_type != RTM_GET || info.rti_info[RTAX_GATEWAY])) {
rt = rt_mpath_matchgate(rt, info.rti_info[RTAX_GATEWAY]);
if (!rt) {
RIB_RUNLOCK(rnh);
senderr(ESRCH);
}
}
#endif
/*
* If performing proxied L2 entry insertion, and
* the actual PPP host entry is found, perform
* another search to retrieve the prefix route of
* the local end point of the PPP link.
*/
if (rtm->rtm_flags & RTF_ANNOUNCE) {
struct sockaddr laddr;
if (rt->rt_ifp != NULL &&
rt->rt_ifp->if_type == IFT_PROPVIRTUAL) {
struct epoch_tracker et;
struct ifaddr *ifa;
NET_EPOCH_ENTER(et);
ifa = ifa_ifwithnet(info.rti_info[RTAX_DST], 1,
RT_ALL_FIBS);
NET_EPOCH_EXIT(et);
if (ifa != NULL)
rt_maskedcopy(ifa->ifa_addr,
&laddr,
ifa->ifa_netmask);
} else
rt_maskedcopy(rt->rt_ifa->ifa_addr,
&laddr,
rt->rt_ifa->ifa_netmask);
/*
* refactor rt and no lock operation necessary
*/
rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr,
&rnh->head);
if (rt == NULL) {
RIB_RUNLOCK(rnh);
senderr(ESRCH);
}
}
RT_LOCK(rt);
RT_ADDREF(rt);
RIB_RUNLOCK(rnh);
error = handle_rtm_get(&info, fibnum, rtm, &rt);
if (error != 0)
senderr(error);
report:
RT_LOCK_ASSERT(rt);
if ((rt->rt_flags & RTF_HOST) == 0
? jailed_without_vnet(curthread->td_ucred)
: prison_if(curthread->td_ucred,
rt_key(rt)) != 0) {
if (!can_export_rte(curthread->td_ucred, rt)) {
RT_UNLOCK(rt);
senderr(ESRCH);
}
info.rti_info[RTAX_DST] = rt_key(rt);
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt),
rt_mask(rt), &ss);
info.rti_info[RTAX_GENMASK] = 0;
if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
ifp = rt->rt_ifp;
if (ifp) {
info.rti_info[RTAX_IFP] =
ifp->if_addr->ifa_addr;
error = rtm_get_jailed(&info, ifp, rt,
&saun, curthread->td_ucred);
if (error != 0) {
RT_UNLOCK(rt);
senderr(error);
}
if (ifp->if_flags & IFF_POINTOPOINT)
info.rti_info[RTAX_BRD] =
rt->rt_ifa->ifa_dstaddr;
rtm->rtm_index = ifp->if_index;
} else {
info.rti_info[RTAX_IFP] = NULL;
info.rti_info[RTAX_IFA] = NULL;
}
} else if ((ifp = rt->rt_ifp) != NULL) {
rtm->rtm_index = ifp->if_index;
}
/* Check if we need to realloc storage */
rtsock_msg_buffer(rtm->rtm_type, &info, NULL, &len);
if (len > alloc_len) {
struct rt_msghdr *new_rtm;
new_rtm = malloc(len, M_TEMP, M_NOWAIT);
if (new_rtm == NULL) {
RT_UNLOCK(rt);
senderr(ENOBUFS);
}
bcopy(rtm, new_rtm, rtm->rtm_msglen);
free(rtm, M_TEMP);
rtm = new_rtm;
alloc_len = len;
}
w.w_tmem = (caddr_t)rtm;
w.w_tmemsize = alloc_len;
rtsock_msg_buffer(rtm->rtm_type, &info, &w, &len);
if (rt->rt_flags & RTF_GWFLAG_COMPAT)
rtm->rtm_flags = RTF_GATEWAY |
(rt->rt_flags & ~RTF_GWFLAG_COMPAT);
else
rtm->rtm_flags = rt->rt_flags;
rt_getmetrics(rt, &rtm->rtm_rmx);
rtm->rtm_addrs = info.rti_addrs;
error = update_rtm_from_rte(&info, &rtm, alloc_len, rt);
/*
* Note that some sockaddr pointers may have changed to
* point to memory outsize @rtm. Some may be pointing
* to the on-stack variables.
* Given that, any pointer in @info CANNOT BE USED.
*/
/*
* scopeid deembedding has been performed while
* writing updated rtm in rtsock_msg_buffer().
* With that in mind, skip deembedding procedure below.
*/
rti_need_deembed = 0;
RT_UNLOCK(rt);
if (error != 0)
senderr(error);
break;
default:
@ -903,22 +982,9 @@ route_output(struct mbuf *m, struct socket *so, ...)
NET_EPOCH_EXIT(et);
if (rt != NULL)
RTFREE(rt);
/*
* Check to see if we don't want our own messages.
*/
if ((so->so_options & SO_USELOOPBACK) == 0) {
if (V_route_cb.any_count <= 1) {
if (rtm != NULL)
free(rtm, M_TEMP);
m_freem(m);
return (error);
}
/* There is another listener, so construct message */
rp = sotorawcb(so);
}
if (rtm != NULL) {
#ifdef INET6
if (rtm != NULL) {
if (rti_need_deembed) {
/* sin6_scope_id is recovered before sending rtm. */
sin6 = (struct sockaddr_in6 *)&ss;
@ -933,9 +999,41 @@ route_output(struct mbuf *m, struct socket *so, ...)
sizeof(*sin6));
}
}
}
#endif
if (error != 0)
rtm->rtm_errno = error;
send_rtm_reply(so, rtm, m, saf, fibnum, error);
return (error);
}
/*
* Sends the prepared reply message in @rtm to all rtsock clients.
* Frees @m and @rtm.
*
*/
static void
send_rtm_reply(struct socket *so, struct rt_msghdr *rtm, struct mbuf *m,
sa_family_t saf, u_int fibnum, int rtm_errno)
{
struct rawcb *rp = NULL;
/*
* Check to see if we don't want our own messages.
*/
if ((so->so_options & SO_USELOOPBACK) == 0) {
if (V_route_cb.any_count <= 1) {
if (rtm != NULL)
free(rtm, M_TEMP);
m_freem(m);
return;
}
/* There is another listener, so construct message */
rp = sotorawcb(so);
}
if (rtm != NULL) {
if (rtm_errno!= 0)
rtm->rtm_errno = rtm_errno;
else
rtm->rtm_flags |= RTF_DONE;
@ -963,10 +1061,9 @@ route_output(struct mbuf *m, struct socket *so, ...)
} else
rt_dispatch(m, saf);
}
return (error);
}
static void
rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out)
{
@ -1562,6 +1659,22 @@ rt_dispatch(struct mbuf *m, sa_family_t saf)
netisr_queue(NETISR_ROUTE, m); /* mbuf is free'd on failure. */
}
/*
* Checks if rte can be exported v.r.t jails/vnets.
*
* Returns 1 if it can, 0 otherwise.
*/
static int
can_export_rte(struct ucred *td_ucred, const struct rtentry *rt)
{
if ((rt->rt_flags & RTF_HOST) == 0
? jailed_without_vnet(td_ucred)
: prison_if(td_ucred, rt_key_const(rt)) != 0)
return (0);
return (1);
}
/*
* This is used in dumping the kernel table via sysctl().
*/
@ -1578,9 +1691,7 @@ sysctl_dumpentry(struct radix_node *rn, void *vw)
if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg))
return 0;
if ((rt->rt_flags & RTF_HOST) == 0
? jailed_without_vnet(w->w_req->td->td_ucred)
: prison_if(w->w_req->td->td_ucred, rt_key(rt)) != 0)
if (!can_export_rte(w->w_req->td->td_ucred, rt))
return (0);
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] = rt_key(rt);

View File

@ -414,7 +414,7 @@ int prison_restrict_ip6(struct prison *, struct in6_addr *);
int prison_qcmp_v6(const void *, const void *);
#endif
int prison_check_af(struct ucred *cred, int af);
int prison_if(struct ucred *cred, struct sockaddr *sa);
int prison_if(struct ucred *cred, const struct sockaddr *sa);
char *prison_name(struct prison *, struct prison *);
int prison_priv_check(struct ucred *cred, int priv);
int sysctl_jail_param(SYSCTL_HANDLER_ARGS);