Changeset View
Changeset View
Standalone View
Standalone View
sys/net/rtsock.c
Show All 26 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
* | * | ||||
* @(#)rtsock.c 8.7 (Berkeley) 10/12/95 | * @(#)rtsock.c 8.7 (Berkeley) 10/12/95 | ||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include "opt_ddb.h" | #include "opt_ddb.h" | ||||
#include "opt_mpath.h" | #include "opt_route_mpath.h" | ||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/domain.h> | #include <sys/domain.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
Show All 19 Lines | |||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <net/if_llatbl.h> | #include <net/if_llatbl.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/netisr.h> | #include <net/netisr.h> | ||||
#include <net/raw_cb.h> | #include <net/raw_cb.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/route_var.h> | #include <net/route_var.h> | ||||
#include <net/route/rtentry_var.h> | |||||
#include <net/route/shared.h> | |||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/if_ether.h> | #include <netinet/if_ether.h> | ||||
#include <netinet/ip_carp.h> | #include <netinet/ip_carp.h> | ||||
#ifdef INET6 | #ifdef INET6 | ||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
#endif | #endif | ||||
#include <net/route/nhop.h> | |||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <compat/freebsd32/freebsd32.h> | #include <compat/freebsd32/freebsd32.h> | ||||
struct if_msghdr32 { | struct if_msghdr32 { | ||||
uint16_t ifm_msglen; | uint16_t ifm_msglen; | ||||
uint8_t ifm_version; | uint8_t ifm_version; | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
struct mtx rtsock_mtx; | struct mtx rtsock_mtx; | ||||
MTX_SYSINIT(rtsock, &rtsock_mtx, "rtsock route_cb lock", MTX_DEF); | MTX_SYSINIT(rtsock, &rtsock_mtx, "rtsock route_cb lock", MTX_DEF); | ||||
#define RTSOCK_LOCK() mtx_lock(&rtsock_mtx) | #define RTSOCK_LOCK() mtx_lock(&rtsock_mtx) | ||||
#define RTSOCK_UNLOCK() mtx_unlock(&rtsock_mtx) | #define RTSOCK_UNLOCK() mtx_unlock(&rtsock_mtx) | ||||
#define RTSOCK_LOCK_ASSERT() mtx_assert(&rtsock_mtx, MA_OWNED) | #define RTSOCK_LOCK_ASSERT() mtx_assert(&rtsock_mtx, MA_OWNED) | ||||
static SYSCTL_NODE(_net, OID_AUTO, route, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, | SYSCTL_NODE(_net, OID_AUTO, route, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); | ||||
""); | |||||
struct walkarg { | struct walkarg { | ||||
int w_tmemsize; | int w_tmemsize; | ||||
int w_op, w_arg; | int w_op, w_arg; | ||||
caddr_t w_tmem; | caddr_t w_tmem; | ||||
struct sysctl_req *w_req; | struct sysctl_req *w_req; | ||||
}; | }; | ||||
struct nh_walkarg; | |||||
static void rts_input(struct mbuf *m); | static void rts_input(struct mbuf *m); | ||||
static struct mbuf *rtsock_msg_mbuf(int type, struct rt_addrinfo *rtinfo); | static struct mbuf *rtsock_msg_mbuf(int type, struct rt_addrinfo *rtinfo); | ||||
static int rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, | static int rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, | ||||
struct walkarg *w, int *plen); | struct walkarg *w, int *plen); | ||||
static int rt_xaddrs(caddr_t cp, caddr_t cplim, | static int rt_xaddrs(caddr_t cp, caddr_t cplim, | ||||
struct rt_addrinfo *rtinfo); | struct rt_addrinfo *rtinfo); | ||||
static int sysctl_dump_rt_nhop(struct nhop_object *nh, uint32_t rt_weight, | |||||
struct nh_walkarg *nw); | |||||
static int sysctl_dumpentry(struct radix_node *rn, void *vw); | static int sysctl_dumpentry(struct radix_node *rn, void *vw); | ||||
static int sysctl_iflist(int af, struct walkarg *w); | static int sysctl_iflist(int af, struct walkarg *w); | ||||
static int sysctl_ifmalist(int af, struct walkarg *w); | static int sysctl_ifmalist(int af, struct walkarg *w); | ||||
static int route_output(struct mbuf *m, struct socket *so, ...); | static int route_output(struct mbuf *m, struct socket *so, ...); | ||||
static void rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out); | static void rt_getmetrics(const struct rtentry *rt, const struct nhop_object *nh, | ||||
uint32_t weight, struct rt_metrics *out); | |||||
static void rt_dispatch(struct mbuf *, sa_family_t); | static void rt_dispatch(struct mbuf *, sa_family_t); | ||||
static struct sockaddr *rtsock_fix_netmask(struct sockaddr *dst, | static struct sockaddr *rtsock_fix_netmask(struct sockaddr *dst, | ||||
struct sockaddr *smask, struct sockaddr_storage *dmask); | struct sockaddr *smask, struct sockaddr_in6 *dmask); | ||||
static int handle_rtm_get(struct rt_addrinfo *info, u_int fibnum, | static int handle_rtm_get(u_int fibnum, struct rt_addrinfo *info, | ||||
struct rt_msghdr *rtm, struct rtentry **ret_nrt); | struct rt_msghdr **prtm, int alloc_len); | ||||
static int update_rtm_from_rte(struct rt_addrinfo *info, | static int | ||||
struct rt_msghdr **prtm, int alloc_len, | update_rtm_from_rte(struct rt_addrinfo *info, struct rt_msghdr **prtm, | ||||
struct rtentry *rt); | int alloc_len, const struct rtentry *rt, struct nhop_object *nh, uint32_t rt_weight); | ||||
static void send_rtm_reply(struct socket *so, struct rt_msghdr *rtm, | static void send_rtm_reply(struct socket *so, struct rt_msghdr *rtm, | ||||
struct mbuf *m, sa_family_t saf, u_int fibnum, | struct mbuf *m, sa_family_t saf, u_int fibnum, | ||||
int rtm_errno); | int rtm_errno); | ||||
static int can_export_rte(struct ucred *td_ucred, const struct rtentry *rt); | static int can_export_rte(struct ucred *td_ucred, const struct rtentry *rt); | ||||
static struct netisr_handler rtsock_nh = { | static struct netisr_handler rtsock_nh = { | ||||
.nh_name = "rtsock", | .nh_name = "rtsock", | ||||
.nh_handler = rts_input, | .nh_handler = rts_input, | ||||
▲ Show 20 Lines • Show All 252 Lines • ▼ Show 20 Lines | union sockaddr_union { | ||||
struct sockaddr sa; | struct sockaddr sa; | ||||
struct sockaddr_in sin; | struct sockaddr_in sin; | ||||
struct sockaddr_in6 sin6; | struct sockaddr_in6 sin6; | ||||
}; | }; | ||||
#endif /* _SOCKADDR_UNION_DEFINED */ | #endif /* _SOCKADDR_UNION_DEFINED */ | ||||
static int | static int | ||||
rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp, | rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp, | ||||
struct rtentry *rt, union sockaddr_union *saun, struct ucred *cred) | const struct nhop_object *nh, union sockaddr_union *saun, struct ucred *cred) | ||||
{ | { | ||||
#if defined(INET) || defined(INET6) | #if defined(INET) || defined(INET6) | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
#endif | #endif | ||||
/* First, see if the returned address is part of the jail. */ | /* First, see if the returned address is part of the jail. */ | ||||
if (prison_if(cred, rt->rt_ifa->ifa_addr) == 0) { | if (prison_if(cred, nh->nh_ifa->ifa_addr) == 0) { | ||||
info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; | info->rti_info[RTAX_IFA] = nh->nh_ifa->ifa_addr; | ||||
return (0); | return (0); | ||||
} | } | ||||
switch (info->rti_info[RTAX_DST]->sa_family) { | switch (info->rti_info[RTAX_DST]->sa_family) { | ||||
#ifdef INET | #ifdef INET | ||||
case AF_INET: | case AF_INET: | ||||
{ | { | ||||
struct in_addr ia; | struct in_addr ia; | ||||
Show All 17 Lines | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
if (!found) { | if (!found) { | ||||
/* | /* | ||||
* As a last resort return the 'default' jail address. | * As a last resort return the 'default' jail address. | ||||
*/ | */ | ||||
ia = ((struct sockaddr_in *)rt->rt_ifa->ifa_addr)-> | ia = ((struct sockaddr_in *)nh->nh_ifa->ifa_addr)-> | ||||
sin_addr; | sin_addr; | ||||
if (prison_get_ip4(cred, &ia) != 0) | if (prison_get_ip4(cred, &ia) != 0) | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
bzero(&saun->sin, sizeof(struct sockaddr_in)); | bzero(&saun->sin, sizeof(struct sockaddr_in)); | ||||
saun->sin.sin_len = sizeof(struct sockaddr_in); | saun->sin.sin_len = sizeof(struct sockaddr_in); | ||||
saun->sin.sin_family = AF_INET; | saun->sin.sin_family = AF_INET; | ||||
saun->sin.sin_addr.s_addr = ia.s_addr; | saun->sin.sin_addr.s_addr = ia.s_addr; | ||||
Show All 26 Lines | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
if (!found) { | if (!found) { | ||||
/* | /* | ||||
* As a last resort return the 'default' jail address. | * As a last resort return the 'default' jail address. | ||||
*/ | */ | ||||
ia6 = ((struct sockaddr_in6 *)rt->rt_ifa->ifa_addr)-> | ia6 = ((struct sockaddr_in6 *)nh->nh_ifa->ifa_addr)-> | ||||
sin6_addr; | sin6_addr; | ||||
if (prison_get_ip6(cred, &ia6) != 0) | if (prison_get_ip6(cred, &ia6) != 0) | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
bzero(&saun->sin6, sizeof(struct sockaddr_in6)); | bzero(&saun->sin6, sizeof(struct sockaddr_in6)); | ||||
saun->sin6.sin6_len = sizeof(struct sockaddr_in6); | saun->sin6.sin6_len = sizeof(struct sockaddr_in6); | ||||
saun->sin6.sin6_family = AF_INET6; | saun->sin6.sin6_family = AF_INET6; | ||||
bcopy(&ia6, &saun->sin6.sin6_addr, sizeof(struct in6_addr)); | bcopy(&ia6, &saun->sin6.sin6_addr, sizeof(struct in6_addr)); | ||||
Show All 13 Lines | |||||
* Fills in @info based on userland-provided @rtm message. | * Fills in @info based on userland-provided @rtm message. | ||||
* | * | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
static int | static int | ||||
fill_addrinfo(struct rt_msghdr *rtm, int len, u_int fibnum, struct rt_addrinfo *info) | fill_addrinfo(struct rt_msghdr *rtm, int len, u_int fibnum, struct rt_addrinfo *info) | ||||
{ | { | ||||
int error; | int error; | ||||
sa_family_t saf; | |||||
/* | |||||
* Starting from here, it is possible | |||||
* to alter original message and insert | |||||
* caller PID and error value. | |||||
*/ | |||||
rtm->rtm_pid = curproc->p_pid; | rtm->rtm_pid = curproc->p_pid; | ||||
info->rti_addrs = rtm->rtm_addrs; | info->rti_addrs = rtm->rtm_addrs; | ||||
info->rti_mflags = rtm->rtm_inits; | info->rti_mflags = rtm->rtm_inits; | ||||
info->rti_rmx = &rtm->rtm_rmx; | info->rti_rmx = &rtm->rtm_rmx; | ||||
/* | /* | ||||
* rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6 | * rt_xaddrs() performs s6_addr[2] := sin6_scope_id for AF_INET6 | ||||
* link-local address because rtrequest requires addresses with | * link-local address because rtrequest requires addresses with | ||||
* embedded scope id. | * embedded scope id. | ||||
*/ | */ | ||||
if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, info)) | if (rt_xaddrs((caddr_t)(rtm + 1), len + (caddr_t)rtm, info)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (rtm->rtm_flags & RTF_RNH_LOCKED) | if (rtm->rtm_flags & RTF_RNH_LOCKED) | ||||
return (EINVAL); | return (EINVAL); | ||||
info->rti_flags = rtm->rtm_flags; | info->rti_flags = rtm->rtm_flags; | ||||
if (info->rti_info[RTAX_DST] == NULL || | if (info->rti_info[RTAX_DST] == NULL || | ||||
info->rti_info[RTAX_DST]->sa_family >= AF_MAX || | info->rti_info[RTAX_DST]->sa_family >= AF_MAX || | ||||
(info->rti_info[RTAX_GATEWAY] != NULL && | (info->rti_info[RTAX_GATEWAY] != NULL && | ||||
info->rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX)) | info->rti_info[RTAX_GATEWAY]->sa_family >= AF_MAX)) | ||||
return (EINVAL); | return (EINVAL); | ||||
saf = info->rti_info[RTAX_DST]->sa_family; | |||||
/* | /* | ||||
* Verify that the caller has the appropriate privilege; RTM_GET | * Verify that the caller has the appropriate privilege; RTM_GET | ||||
* is the only operation the non-superuser is allowed. | * is the only operation the non-superuser is allowed. | ||||
*/ | */ | ||||
if (rtm->rtm_type != RTM_GET) { | if (rtm->rtm_type != RTM_GET) { | ||||
error = priv_check(curthread, PRIV_NET_ROUTE); | error = priv_check(curthread, PRIV_NET_ROUTE); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | fill_addrinfo(struct rt_msghdr *rtm, int len, u_int fibnum, struct rt_addrinfo *info) | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Handles RTM_GET message from routing socket, returning matching rt. | * Handles RTM_GET message from routing socket, returning matching rt. | ||||
* | * | ||||
* Returns: | * Returns: | ||||
* 0 on success, with locked and referenced matching rt in @rt_nrt | * 0 on success, with locked matching rt, nh in @ret_nrt and @ret_nh | ||||
* errno of failure | * errno of failure | ||||
*/ | */ | ||||
static int | static int | ||||
handle_rtm_get(struct rt_addrinfo *info, u_int fibnum, | handle_rtm_get(u_int fibnum, struct rt_addrinfo *info, | ||||
struct rt_msghdr *rtm, struct rtentry **ret_nrt) | struct rt_msghdr **prtm, int alloc_len) | ||||
{ | { | ||||
RIB_RLOCK_TRACKER; | |||||
struct rtentry *rt; | struct rtentry *rt; | ||||
struct rib_head *rnh; | struct nhop_object *nh; | ||||
sa_family_t saf; | uint32_t rt_weight; | ||||
int error; | |||||
saf = info->rti_info[RTAX_DST]->sa_family; | error = rib_lookup_route_netmask(fibnum, info->rti_info[RTAX_DST], | ||||
info->rti_info[RTAX_NETMASK], &rt); | |||||
rnh = rt_tables_get_rnh(fibnum, saf); | if (error != 0) | ||||
if (rnh == NULL) | return (error); | ||||
return (EAFNOSUPPORT); | |||||
RIB_RLOCK(rnh); | /* rt is locked and unreferenced. */ | ||||
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 | #ifdef RADIX_MPATH | ||||
/* | /* | ||||
* for RTM_GET, gate is optional even with multipath. | * for RTM_GET, gate is optional even with multipath. | ||||
* if gate == NULL the first match is returned. | * if gate == NULL the first match is returned. | ||||
* (no need to call rt_mpath_matchgate if gate == NULL) | * (no need to call rt_mpath_matchgate if gate == NULL) | ||||
*/ | */ | ||||
if (rt_mpath_capable(rnh) && info->rti_info[RTAX_GATEWAY]) { | if (rt_mpath_capable(rnh) && info->rti_info[RTAX_GATEWAY]) { | ||||
rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); | rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); | ||||
if (!rt) { | if (!rt) { | ||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
rt_weight = rt->rt_weight; | |||||
nh = rt->rt_nhop; | |||||
#ifdef ROUTE_MPATH | |||||
if (NH_IS_MULTIPATH(nh)) { | |||||
struct weightened_nhop *wn; | |||||
uint32_t num_nhops; | |||||
struct sockaddr *gw; | |||||
nh = NULL; | |||||
gw = info->rti_info[RTAX_GATEWAY]; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)rt->rt_nhop, | |||||
&num_nhops); | |||||
if (gw != NULL) { | |||||
for (uint32_t i = 0; i < num_nhops; i++) { | |||||
if (rib_match_nhop_gw(wn[i].nh, gw)) { | |||||
nh = wn[i].nh; | |||||
rt_weight = wn[i].weight; | |||||
break; | |||||
} | |||||
} | |||||
if (nh == NULL) { | |||||
RT_UNLOCK(rt); | |||||
return (ESRCH); | |||||
} | |||||
} else { | |||||
/* By default, use the first control plane nexthop */ | |||||
nh = wn[0].nh; | |||||
rt_weight = wn[0].weight; | |||||
} | |||||
} | |||||
#endif | |||||
/* | /* | ||||
* If performing proxied L2 entry insertion, and | * If performing proxied L2 entry insertion, and | ||||
* the actual PPP host entry is found, perform | * the actual PPP host entry is found, perform | ||||
* another search to retrieve the prefix route of | * another search to retrieve the prefix route of | ||||
* the local end point of the PPP link. | * the local end point of the PPP link. | ||||
*/ | */ | ||||
/* XXX: fix RTF_ANNOUNCE */ | |||||
#if 0 | |||||
if (rtm->rtm_flags & RTF_ANNOUNCE) { | if (rtm->rtm_flags & RTF_ANNOUNCE) { | ||||
struct sockaddr laddr; | struct sockaddr laddr; | ||||
if (rt->rt_ifp != NULL && | if (rt->rt_ifp != NULL && | ||||
rt->rt_ifp->if_type == IFT_PROPVIRTUAL) { | rt->rt_ifp->if_type == IFT_PROPVIRTUAL) { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
Show All 14 Lines | if (rtm->rtm_flags & RTF_ANNOUNCE) { | ||||
*/ | */ | ||||
rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr, | rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr, | ||||
&rnh->head); | &rnh->head); | ||||
if (rt == NULL) { | if (rt == NULL) { | ||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
} | } | ||||
RT_LOCK(rt); | #endif | ||||
RT_ADDREF(rt); | |||||
RIB_RUNLOCK(rnh); | |||||
*ret_nrt = rt; | if (!can_export_rte(curthread->td_ucred, rt)) { | ||||
RT_UNLOCK(rt); | |||||
return (ESRCH); | |||||
} | |||||
return (0); | error = update_rtm_from_rte(info, prtm, alloc_len, rt, nh, rt_weight); | ||||
RT_UNLOCK(rt); | |||||
if (error != 0) | |||||
printf("%s: ret %d\n", __func__, error); | |||||
return (error); | |||||
} | } | ||||
/* | /* | ||||
* Update sockaddrs, flags, etc in @prtm based on @rt data. | * Update sockaddrs, flags, etc in @prtm based on @rt data. | ||||
* Assumes @rt is locked. | * Assumes @rt is locked. | ||||
* rtm can be reallocated. | |||||
* | * | ||||
* Returns 0 on success, along with pointer to (potentially reallocated) | * Returns 0 on success, along with pointer to (potentially reallocated) | ||||
* rtm. | * rtm. | ||||
* | * | ||||
*/ | */ | ||||
static int | static int | ||||
update_rtm_from_rte(struct rt_addrinfo *info, struct rt_msghdr **prtm, | update_rtm_from_rte(struct rt_addrinfo *info, struct rt_msghdr **prtm, | ||||
int alloc_len, struct rtentry *rt) | int alloc_len, const struct rtentry *rt, struct nhop_object *nh, uint32_t rt_weight) | ||||
{ | { | ||||
struct sockaddr_storage netmask_ss; | struct sockaddr_in6 dst, mask; | ||||
struct walkarg w; | |||||
union sockaddr_union saun; | |||||
struct rt_msghdr *rtm, *orig_rtm = NULL; | |||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct rt_msghdr *rtm, *orig_rtm = NULL; | |||||
struct walkarg w; | |||||
int error, len; | int error, len; | ||||
union sockaddr_union saun; | |||||
RT_LOCK_ASSERT(rt); | RT_LOCK_ASSERT(rt); | ||||
rtm = *prtm; | info->rti_info[RTAX_DST] = rib_get_entry_dst_sa(rt, | ||||
(struct sockaddr *)&dst, sizeof(dst), &error); | |||||
info->rti_info[RTAX_DST] = rt_key(rt); | if (error != 0) | ||||
info->rti_info[RTAX_GATEWAY] = rt->rt_gateway; | return (error); | ||||
info->rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), | info->rti_info[RTAX_NETMASK] = rib_get_entry_netmask_sa(rt, | ||||
rt_mask(rt), &netmask_ss); | (struct sockaddr *)&mask, sizeof(mask), &error); | ||||
if (error != 0) | |||||
return (error); | |||||
info->rti_info[RTAX_GENMASK] = 0; | info->rti_info[RTAX_GENMASK] = 0; | ||||
ifp = rt->rt_ifp; | info->rti_info[RTAX_GATEWAY] = &nh->gw_sa; | ||||
ifp = nh->nh_ifp; | |||||
rtm = *prtm; | |||||
if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { | if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { | ||||
if (ifp) { | if (ifp) { | ||||
info->rti_info[RTAX_IFP] = | info->rti_info[RTAX_IFP] = | ||||
ifp->if_addr->ifa_addr; | ifp->if_addr->ifa_addr; | ||||
error = rtm_get_jailed(info, ifp, rt, | error = rtm_get_jailed(info, ifp, nh, | ||||
&saun, curthread->td_ucred); | &saun, curthread->td_ucred); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
if (ifp->if_flags & IFF_POINTOPOINT) | if (ifp->if_flags & IFF_POINTOPOINT) | ||||
info->rti_info[RTAX_BRD] = | info->rti_info[RTAX_BRD] = | ||||
rt->rt_ifa->ifa_dstaddr; | nh->nh_ifa->ifa_dstaddr; | ||||
rtm->rtm_index = ifp->if_index; | rtm->rtm_index = ifp->if_index; | ||||
} else { | } else { | ||||
info->rti_info[RTAX_IFP] = NULL; | info->rti_info[RTAX_IFP] = NULL; | ||||
info->rti_info[RTAX_IFA] = NULL; | info->rti_info[RTAX_IFA] = NULL; | ||||
} | } | ||||
} else if (ifp != NULL) | } else if (ifp != NULL) | ||||
rtm->rtm_index = ifp->if_index; | rtm->rtm_index = ifp->if_index; | ||||
Show All 15 Lines | if (len > alloc_len) { | ||||
* data referencing it. | * data referencing it. | ||||
*/ | */ | ||||
} | } | ||||
w.w_tmem = (caddr_t)rtm; | w.w_tmem = (caddr_t)rtm; | ||||
w.w_tmemsize = alloc_len; | w.w_tmemsize = alloc_len; | ||||
rtsock_msg_buffer(rtm->rtm_type, info, &w, &len); | rtsock_msg_buffer(rtm->rtm_type, info, &w, &len); | ||||
if (rt->rt_flags & RTF_GWFLAG_COMPAT) | int rt_flags = rib_get_entry_rtflags(rt, nh); | ||||
/* XXX: Eliminate RTF_GWFLAG_COMPAT */ | |||||
if (rt->rte_flags & RTF_GWFLAG_COMPAT) | |||||
rtm->rtm_flags = RTF_GATEWAY | | rtm->rtm_flags = RTF_GATEWAY | | ||||
(rt->rt_flags & ~RTF_GWFLAG_COMPAT); | (rt_flags & ~RTF_GWFLAG_COMPAT); | ||||
else | else | ||||
rtm->rtm_flags = rt->rt_flags; | rtm->rtm_flags = rt_flags; | ||||
rt_getmetrics(rt, &rtm->rtm_rmx); | rt_getmetrics(rt, nh, rt_weight, &rtm->rtm_rmx); | ||||
rtm->rtm_addrs = info->rti_addrs; | rtm->rtm_addrs = info->rti_addrs; | ||||
if (orig_rtm != NULL) | if (orig_rtm != NULL) | ||||
free(orig_rtm, M_TEMP); | free(orig_rtm, M_TEMP); | ||||
*prtm = rtm; | *prtm = rtm; | ||||
return (0); | return (0); | ||||
} | } | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
route_output(struct mbuf *m, struct socket *so, ...) | route_output(struct mbuf *m, struct socket *so, ...) | ||||
{ | { | ||||
struct rt_msghdr *rtm = NULL; | struct rt_msghdr *rtm = NULL; | ||||
struct rtentry *rt = NULL; | |||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct sockaddr_storage ss; | |||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct sockaddr_storage ss; | |||||
struct sockaddr_in6 *sin6; | struct sockaddr_in6 *sin6; | ||||
int i, rti_need_deembed = 0; | int i, rti_need_deembed = 0; | ||||
#endif | #endif | ||||
int alloc_len = 0, len, error = 0, fibnum; | int alloc_len = 0, len, error = 0, fibnum; | ||||
sa_family_t saf = AF_UNSPEC; | sa_family_t saf = AF_UNSPEC; | ||||
struct walkarg w; | struct walkarg w; | ||||
fibnum = so->so_fibnum; | fibnum = so->so_fibnum; | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (rtm->rtm_flags & RTF_LLDATA) { | ||||
error = lla_rt_output(rtm, &info); | error = lla_rt_output(rtm, &info); | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if (error == 0) | if (error == 0) | ||||
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | ||||
#endif | #endif | ||||
goto flush; | goto flush; | ||||
} | } | ||||
switch (rtm->rtm_type) { | struct rib_cmd_info rc; | ||||
struct rtentry *saved_nrt; | bzero(&rc, sizeof(rc)); | ||||
switch (rtm->rtm_type) { | |||||
case RTM_ADD: | case RTM_ADD: | ||||
case RTM_CHANGE: | case RTM_CHANGE: | ||||
if (rtm->rtm_type == RTM_ADD) { | if (rtm->rtm_type == RTM_ADD) { | ||||
if (info.rti_info[RTAX_GATEWAY] == NULL) | if (info.rti_info[RTAX_GATEWAY] == NULL) | ||||
senderr(EINVAL); | senderr(EINVAL); | ||||
} | error = rib_add_route(fibnum, &info, &rc); | ||||
saved_nrt = NULL; | } else | ||||
error = rtrequest1_fib(rtm->rtm_type, &info, &saved_nrt, | error = rib_change_route(fibnum, &info, &rc); | ||||
fibnum); | if (error == 0) { | ||||
if (error == 0 && saved_nrt != NULL) { | |||||
#ifdef INET6 | #ifdef INET6 | ||||
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | ||||
#endif | #endif | ||||
RT_LOCK(saved_nrt); | #ifdef ROUTE_MPATH | ||||
rtm->rtm_index = saved_nrt->rt_ifp->if_index; | if (NH_IS_MULTIPATH(rc.nh_new) && rc.mask_changed) { | ||||
RT_REMREF(saved_nrt); | uint32_t num_nhops, idx; | ||||
RT_UNLOCK(saved_nrt); | struct weightened_nhop *wn; | ||||
/* Find the index of the added nhop. */ | |||||
idx = ffsll(rc.mask_changed) - 1; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)rc.nh_new, | |||||
&num_nhops); | |||||
rtm->rtm_index = wn[idx].nh->nh_ifp->if_index; | |||||
} else | |||||
#endif | |||||
rtm->rtm_index = rc.nh_new->nh_ifp->if_index; | |||||
} | } | ||||
break; | break; | ||||
case RTM_DELETE: | case RTM_DELETE: | ||||
saved_nrt = NULL; | error = rib_del_route(fibnum, &info, &rc); | ||||
error = rtrequest1_fib(RTM_DELETE, &info, &saved_nrt, fibnum); | |||||
if (error == 0) { | if (error == 0) { | ||||
RT_LOCK(saved_nrt); | /* XXX: mpath */ | ||||
rt = saved_nrt; | if (can_export_rte(curthread->td_ucred, rc.rt)) { | ||||
RT_LOCK(rc.rt); | |||||
error = update_rtm_from_rte(&info, &rtm, | |||||
alloc_len, rc.rt, rc.nh_old, | |||||
rc.rt->rt_weight); | |||||
RT_UNLOCK(rc.rt); | |||||
} else | |||||
error = ESRCH; | |||||
goto report; | goto report; | ||||
} | } | ||||
#ifdef INET6 | #ifdef INET6 | ||||
/* rt_msg2() will not be used when RTM_DELETE fails. */ | /* rt_msg2() will not be used when RTM_DELETE fails. */ | ||||
rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | rti_need_deembed = (V_deembed_scopeid) ? 1 : 0; | ||||
#endif | #endif | ||||
break; | break; | ||||
case RTM_GET: | case RTM_GET: | ||||
error = handle_rtm_get(&info, fibnum, rtm, &rt); | /* XXX: verify deembed on errors */ | ||||
error = handle_rtm_get(fibnum, &info, &rtm, alloc_len); | |||||
if (error != 0) | if (error != 0) | ||||
senderr(error); | senderr(error); | ||||
report: | report: | ||||
RT_LOCK_ASSERT(rt); | |||||
if (!can_export_rte(curthread->td_ucred, rt)) { | |||||
RT_UNLOCK(rt); | |||||
senderr(ESRCH); | |||||
} | |||||
error = update_rtm_from_rte(&info, &rtm, alloc_len, rt); | |||||
/* | /* | ||||
* Note that some sockaddr pointers may have changed to | * Note that some sockaddr pointers may have changed to | ||||
* point to memory outsize @rtm. Some may be pointing | * point to memory outsize @rtm. Some may be pointing | ||||
* to the on-stack variables. | * to the on-stack variables. | ||||
* Given that, any pointer in @info CANNOT BE USED. | * Given that, any pointer in @info CANNOT BE USED. | ||||
*/ | */ | ||||
/* | /* | ||||
* scopeid deembedding has been performed while | * scopeid deembedding has been performed while | ||||
* writing updated rtm in rtsock_msg_buffer(). | * writing updated rtm in rtsock_msg_buffer(). | ||||
* With that in mind, skip deembedding procedure below. | * With that in mind, skip deembedding procedure below. | ||||
*/ | */ | ||||
#ifdef INET6 | #ifdef INET6 | ||||
rti_need_deembed = 0; | rti_need_deembed = 0; | ||||
#endif | #endif | ||||
RT_UNLOCK(rt); | |||||
if (error != 0) | if (error != 0) | ||||
senderr(error); | senderr(error); | ||||
break; | break; | ||||
default: | default: | ||||
senderr(EOPNOTSUPP); | senderr(EOPNOTSUPP); | ||||
} | } | ||||
flush: | flush: | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
if (rt != NULL) | |||||
RTFREE(rt); | |||||
#ifdef INET6 | #ifdef INET6 | ||||
if (rtm != NULL) { | if (rtm != NULL) { | ||||
if (rti_need_deembed) { | if (rti_need_deembed) { | ||||
/* sin6_scope_id is recovered before sending rtm. */ | /* sin6_scope_id is recovered before sending rtm. */ | ||||
sin6 = (struct sockaddr_in6 *)&ss; | sin6 = (struct sockaddr_in6 *)&ss; | ||||
for (i = 0; i < RTAX_MAX; i++) { | for (i = 0; i < RTAX_MAX; i++) { | ||||
if (info.rti_info[i] == NULL) | if (info.rti_info[i] == NULL) | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | if (rp) { | ||||
rp->rcb_proto.sp_family = family; | rp->rcb_proto.sp_family = family; | ||||
} else | } else | ||||
rt_dispatch(m, saf); | rt_dispatch(m, saf); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out) | rt_getmetrics(const struct rtentry *rt, const struct nhop_object *nh, | ||||
uint32_t rt_weight, struct rt_metrics *out) | |||||
{ | { | ||||
bzero(out, sizeof(*out)); | bzero(out, sizeof(*out)); | ||||
out->rmx_mtu = rt->rt_mtu; | out->rmx_mtu = nh->nh_mtu; | ||||
out->rmx_weight = rt->rt_weight; | out->rmx_weight = rt_weight; | ||||
out->rmx_pksent = counter_u64_fetch(rt->rt_pksent); | out->rmx_pksent = nhop_get_idx(nh); | ||||
/* Kernel -> userland timebase conversion. */ | /* Kernel -> userland timebase conversion. */ | ||||
out->rmx_expire = rt->rt_expire ? | out->rmx_expire = rt->rt_expire ? | ||||
rt->rt_expire - time_uptime + time_second : 0; | rt->rt_expire - time_uptime + time_second : 0; | ||||
} | } | ||||
/* | /* | ||||
* Extract the addresses of the passed sockaddrs. | * Extract the addresses of the passed sockaddrs. | ||||
* Do a little sanity checking so as to avoid bad memory references. | * Do a little sanity checking so as to avoid bad memory references. | ||||
Show All 34 Lines | #endif | ||||
rtinfo->rti_info[i] = sa; | rtinfo->rti_info[i] = sa; | ||||
cp += SA_SIZE(sa); | cp += SA_SIZE(sa); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Fill in @dmask with valid netmask leaving original @smask | * Fill in @dmask with valid netmask leaving original @smask | ||||
* intact. Mostly used with radix netmasks. | * intact. Used with radix-originated netmasks. | ||||
*/ | */ | ||||
static struct sockaddr * | static struct sockaddr * | ||||
rtsock_fix_netmask(struct sockaddr *dst, struct sockaddr *smask, | rtsock_fix_netmask(struct sockaddr *dst, struct sockaddr *smask, | ||||
struct sockaddr_storage *dmask) | struct sockaddr_in6 *dmask) | ||||
{ | { | ||||
if (dst == NULL || smask == NULL) | if (dst == NULL || smask == NULL) | ||||
return (NULL); | return (NULL); | ||||
memset(dmask, 0, dst->sa_len); | if (dst->sa_len > sizeof(struct sockaddr_in6)) { | ||||
memcpy(dmask, smask, smask->sa_len); | printf("NETMASK SA_LEN: %d\n", dst->sa_len); | ||||
dmask->ss_len = dst->sa_len; | return (NULL); | ||||
dmask->ss_family = dst->sa_family; | } | ||||
bzero(dmask, dst->sa_len); | |||||
dmask->sin6_len = dst->sa_len; | |||||
dmask->sin6_family = dst->sa_family; | |||||
switch (dst->sa_family) { | |||||
case AF_INET: | |||||
((struct sockaddr_in *)dmask)->sin_addr = | |||||
((struct sockaddr_in *)smask)->sin_addr; | |||||
return ((struct sockaddr *)dmask); | return ((struct sockaddr *)dmask); | ||||
case AF_INET6: | |||||
((struct sockaddr_in6 *)dmask)->sin6_addr = | |||||
((struct sockaddr_in6 *)smask)->sin6_addr; | |||||
return ((struct sockaddr *)dmask); | |||||
} | } | ||||
return (smask); | |||||
} | |||||
#ifdef COMPAT_FREEBSD32 | |||||
#define SA_SIZE_COMPAT(_sa, _compat32) { \ | |||||
if (_compat32) \ | |||||
SA_SIZE32(sa); \ | |||||
else \ | |||||
SA_SIZE(_sa); \ | |||||
} | |||||
#else | |||||
#define SA_SIZE_COMPAT(_sa, _compat32) SA_SIZE(_sa) | |||||
#endif | |||||
#if 0 | |||||
inline static struct sockaddr * | |||||
prepare_sockaddr(struct rt_addrinfo *info, int addr, struct sockaddr *buf, | |||||
int buflen, int *sa_len, int compat32, int deembed) | |||||
{ | |||||
struct sockaddr *sa = info->rti_info[addr]; | |||||
if (addr = RTAX_NETMASK) { | |||||
struct sockaddr *dst = info->rti_info[RTAX_DST]; | |||||
*sa_len = SA_SIZE_COMPAT(dst, compat32); | |||||
if (buf == NULL) | |||||
return (NULL); | |||||
return (rtsock_fix_netmask(dst, sa, buf)); | |||||
} | |||||
*sa_len = SA_SIZE_COMPAT(sa, compat32); | |||||
if (buf == NULL) | |||||
return (NULL); | |||||
#ifdef INET6 | |||||
if ((sa->sa_family == AF_INET6) && deembed) { | |||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; | |||||
if ((IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || | |||||
IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))){ | |||||
memcpy(buf, sin6, sin6->sin6_len); | |||||
} | |||||
} | |||||
#else | |||||
return (sa); | |||||
#endif | |||||
} | |||||
#endif | |||||
/* | /* | ||||
* Writes information related to @rtinfo object to newly-allocated mbuf. | * Writes information related to @rtinfo object to newly-allocated mbuf. | ||||
* Assumes MCLBYTES is enough to construct any message. | * Assumes MCLBYTES is enough to construct any message. | ||||
* Used for OS notifications of vaious events (if/ifa announces,etc) | * Used for OS notifications of vaious events (if/ifa announces,etc) | ||||
* | * | ||||
* Returns allocated mbuf or NULL on failure. | * Returns allocated mbuf or NULL on failure. | ||||
*/ | */ | ||||
static struct mbuf * | static struct mbuf * | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
m_copyback(m, len, dlen, (caddr_t)sa); | m_copyback(m, len, dlen, (caddr_t)sa); | ||||
len += dlen; | len += dlen; | ||||
} | } | ||||
if (m->m_pkthdr.len != len) { | if (m->m_pkthdr.len != len) { | ||||
m_freem(m); | m_freem(m); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | |||||
* The following 3 fields are the only fields shared | |||||
* by the rtsock messages. | |||||
*/ | |||||
rtm->rtm_msglen = len; | rtm->rtm_msglen = len; | ||||
rtm->rtm_version = RTM_VERSION; | rtm->rtm_version = RTM_VERSION; | ||||
rtm->rtm_type = type; | rtm->rtm_type = type; | ||||
return (m); | return (m); | ||||
} | } | ||||
/* | /* | ||||
* Writes information related to @rtinfo object to preallocated buffer. | * Writes information related to @rtinfo object to preallocated buffer. | ||||
* Stores needed size in @plen. If @w is NULL, calculates size without | * Stores needed size in @plen. If @w is NULL, calculates size without | ||||
* writing. | * writing. | ||||
* Used for sysctl dumps and rtsock answers (RTM_DEL/RTM_GET) generation. | * Used for sysctl dumps and rtsock answers (RTM_DEL/RTM_GET) generation. | ||||
▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | |||||
rtsock_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) | rtsock_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) | ||||
{ | { | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct sockaddr *sa; | struct sockaddr *sa; | ||||
int ncmd; | int ncmd; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct ifa_msghdr *ifam; | struct ifa_msghdr *ifam; | ||||
struct ifnet *ifp = ifa->ifa_ifp; | struct ifnet *ifp = ifa->ifa_ifp; | ||||
struct sockaddr_storage ss; | struct sockaddr_in6 ss; | ||||
if (V_route_cb.any_count == 0) | if (V_route_cb.any_count == 0) | ||||
return (0); | return (0); | ||||
ncmd = cmd == RTM_ADD ? RTM_NEWADDR : RTM_DELADDR; | ncmd = cmd == RTM_ADD ? RTM_NEWADDR : RTM_DELADDR; | ||||
bzero((caddr_t)&info, sizeof(info)); | bzero((caddr_t)&info, sizeof(info)); | ||||
info.rti_info[RTAX_IFA] = sa = ifa->ifa_addr; | info.rti_info[RTAX_IFA] = sa = ifa->ifa_addr; | ||||
Show All 23 Lines | |||||
* Announce route addition/removal to rtsock based on @rt data. | * Announce route addition/removal to rtsock based on @rt data. | ||||
* Callers are advives to use rt_routemsg() instead of using this | * Callers are advives to use rt_routemsg() instead of using this | ||||
* function directly. | * function directly. | ||||
* Assume @rt data is consistent. | * Assume @rt data is consistent. | ||||
* | * | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
int | int | ||||
rtsock_routemsg(int cmd, struct rtentry *rt, struct ifnet *ifp, int rti_addrs, | rtsock_routemsg(int cmd, struct rtentry *rt, struct nhop_object *nh, int fibnum) | ||||
int fibnum) | |||||
{ | { | ||||
struct sockaddr_storage ss; | struct sockaddr_in6 ss; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
if (V_route_cb.any_count == 0) | if (V_route_cb.any_count == 0) | ||||
return (0); | return (0); | ||||
bzero((caddr_t)&info, sizeof(info)); | bzero((caddr_t)&info, sizeof(info)); | ||||
info.rti_info[RTAX_DST] = rt_key(rt); | info.rti_info[RTAX_DST] = rt_key(rt); | ||||
info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), rt_mask(rt), &ss); | info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), rt_mask(rt), &ss); | ||||
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; | info.rti_info[RTAX_GATEWAY] = &nh->gw_sa; | ||||
info.rti_flags = rt->rt_flags; | info.rti_flags = rib_get_entry_rtflags(rt, nh); | ||||
info.rti_ifp = ifp; | info.rti_ifp = nh->nh_aifp; | ||||
return (rtsock_routemsg_info(cmd, &info, fibnum)); | return (rtsock_routemsg_info(cmd, &info, fibnum)); | ||||
} | } | ||||
int | int | ||||
rtsock_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum) | rtsock_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum) | ||||
{ | { | ||||
struct rt_msghdr *rtm; | struct rt_msghdr *rtm; | ||||
▲ Show 20 Lines • Show All 183 Lines • ▼ Show 20 Lines | |||||
* Checks if rte can be exported v.r.t jails/vnets. | * Checks if rte can be exported v.r.t jails/vnets. | ||||
* | * | ||||
* Returns 1 if it can, 0 otherwise. | * Returns 1 if it can, 0 otherwise. | ||||
*/ | */ | ||||
static int | static int | ||||
can_export_rte(struct ucred *td_ucred, const struct rtentry *rt) | can_export_rte(struct ucred *td_ucred, const struct rtentry *rt) | ||||
{ | { | ||||
if ((rt->rt_flags & RTF_HOST) == 0 | if (!RT_IS_HOST_ROUTE(rt) | ||||
? jailed_without_vnet(td_ucred) | ? jailed_without_vnet(td_ucred) | ||||
: prison_if(td_ucred, rt_key_const(rt)) != 0) | : prison_if(td_ucred, rt_key_const(rt)) != 0) | ||||
return (0); | return (0); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* This is used in dumping the kernel table via sysctl(). | * This is used in dumping the kernel table via sysctl(). | ||||
*/ | */ | ||||
struct nh_walkarg { | |||||
struct walkarg *w; | |||||
struct rtentry *rt; | |||||
}; | |||||
static int | static int | ||||
sysctl_dumpentry(struct radix_node *rn, void *vw) | sysctl_dumpentry(struct radix_node *rn, void *vw) | ||||
{ | { | ||||
struct walkarg *w = vw; | struct walkarg *w = vw; | ||||
struct rtentry *rt = (struct rtentry *)rn; | struct rtentry *rt = (struct rtentry *)rn; | ||||
int error = 0, size; | struct nhop_object *nh; | ||||
struct rt_addrinfo info; | struct nh_walkarg nw; | ||||
struct sockaddr_storage ss; | int rt_flags; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) | nh = rt->rt_nhop; | ||||
rt_flags = rib_get_entry_rtflags(rt, nh); | |||||
if (w->w_op == NET_RT_FLAGS && !(rt_flags & w->w_arg)) | |||||
return 0; | return 0; | ||||
if (!can_export_rte(w->w_req->td->td_ucred, rt)) | if (!can_export_rte(w->w_req->td->td_ucred, rt)) | ||||
return (0); | return (0); | ||||
nw.w = w; | |||||
nw.rt = rt; | |||||
if (!NH_IS_MULTIPATH(nh)) | |||||
return (sysctl_dump_rt_nhop(nh, rib_get_entry_weight(rt), &nw)); | |||||
#ifdef ROUTE_MPATH | |||||
struct weightened_nhop *wn; | |||||
uint32_t num_nhops; | |||||
int error; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops); | |||||
for (uint32_t i = 0; i < num_nhops; i++) { | |||||
error = sysctl_dump_rt_nhop(wn[i].nh, wn[i].weight, &nw); | |||||
if (error != 0) | |||||
return (error); | |||||
} | |||||
#endif | |||||
return (0); | |||||
} | |||||
__noinline static int | |||||
sysctl_dump_rt_nhop(struct nhop_object *nh, uint32_t rt_weight, | |||||
struct nh_walkarg *nw) | |||||
{ | |||||
int error = 0, size; | |||||
struct walkarg *w = nw->w; | |||||
struct rtentry *rt = nw->rt; | |||||
struct rt_addrinfo info; | |||||
struct sockaddr_in6 dst, netmask; | |||||
int rt_flags; | |||||
rt_flags = rib_get_entry_rtflags(rt, nh); | |||||
bzero((caddr_t)&info, sizeof(info)); | bzero((caddr_t)&info, sizeof(info)); | ||||
info.rti_info[RTAX_DST] = rt_key(rt); | rib_get_entry_dst_sa(rt, (struct sockaddr *)&dst, sizeof(dst), &error); | ||||
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; | info.rti_info[RTAX_DST] = rib_get_entry_dst_sa(rt, | ||||
info.rti_info[RTAX_NETMASK] = rtsock_fix_netmask(rt_key(rt), | (struct sockaddr *)&dst, sizeof(dst), &error); | ||||
rt_mask(rt), &ss); | if (error != 0) | ||||
return (error); | |||||
info.rti_info[RTAX_NETMASK] = rib_get_entry_netmask_sa(rt, | |||||
(struct sockaddr *)&netmask, sizeof(netmask), &error); | |||||
if (error != 0) | |||||
return (error); | |||||
info.rti_info[RTAX_GENMASK] = 0; | info.rti_info[RTAX_GENMASK] = 0; | ||||
if (rt->rt_ifp && !(rt->rt_ifp->if_flags & IFF_DYING)) { | info.rti_info[RTAX_GATEWAY] = &nh->gw_sa; | ||||
info.rti_info[RTAX_IFP] = rt->rt_ifp->if_addr->ifa_addr; | if (nh->nh_ifp && !(nh->nh_ifp->if_flags & IFF_DYING)) { | ||||
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; | info.rti_info[RTAX_IFP] = nh->nh_ifp->if_addr->ifa_addr; | ||||
if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) | info.rti_info[RTAX_IFA] = nh->nh_ifa->ifa_addr; | ||||
info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr; | if (nh->nh_ifp->if_flags & IFF_POINTOPOINT) | ||||
info.rti_info[RTAX_BRD] = nh->nh_ifa->ifa_dstaddr; | |||||
} | } | ||||
if ((error = rtsock_msg_buffer(RTM_GET, &info, w, &size)) != 0) | if ((error = rtsock_msg_buffer(RTM_GET, &info, w, &size)) != 0) | ||||
return (error); | return (error); | ||||
if (w->w_req && w->w_tmem) { | if (w->w_req && w->w_tmem) { | ||||
struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem; | struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem; | ||||
bzero(&rtm->rtm_index, | bzero(&rtm->rtm_index, | ||||
sizeof(*rtm) - offsetof(struct rt_msghdr, rtm_index)); | sizeof(*rtm) - offsetof(struct rt_msghdr, rtm_index)); | ||||
if (rt->rt_flags & RTF_GWFLAG_COMPAT) | if (rt_flags & RTF_GWFLAG_COMPAT) | ||||
rtm->rtm_flags = RTF_GATEWAY | | rtm->rtm_flags = RTF_GATEWAY | | ||||
(rt->rt_flags & ~RTF_GWFLAG_COMPAT); | (rt_flags & ~RTF_GWFLAG_COMPAT); | ||||
else | else | ||||
rtm->rtm_flags = rt->rt_flags; | rtm->rtm_flags = rt_flags; | ||||
rt_getmetrics(rt, &rtm->rtm_rmx); | rt_getmetrics(rt, nh, rt_weight, &rtm->rtm_rmx); | ||||
rtm->rtm_index = rt->rt_ifp->if_index; | rtm->rtm_index = nh->nh_ifp->if_index; | ||||
rtm->rtm_addrs = info.rti_addrs; | rtm->rtm_addrs = info.rti_addrs; | ||||
error = SYSCTL_OUT(w->w_req, (caddr_t)rtm, size); | error = SYSCTL_OUT(w->w_req, (caddr_t)rtm, size); | ||||
return (error); | return (error); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
sysctl_iflist(int af, struct walkarg *w) | sysctl_iflist(int af, struct walkarg *w) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct if_data ifd; | struct if_data ifd; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
int len, error = 0; | int len, error = 0; | ||||
struct sockaddr_storage ss; | struct sockaddr_in6 ss; | ||||
bzero((caddr_t)&info, sizeof(info)); | bzero((caddr_t)&info, sizeof(info)); | ||||
bzero(&ifd, sizeof(ifd)); | bzero(&ifd, sizeof(ifd)); | ||||
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { | CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { | ||||
if (w->w_arg && w->w_arg != ifp->if_index) | if (w->w_arg && w->w_arg != ifp->if_index) | ||||
continue; | continue; | ||||
if_data_copy(ifp, &ifd); | if_data_copy(ifp, &ifd); | ||||
ifa = ifp->if_addr; | ifa = ifp->if_addr; | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | sysctl_rtsock(SYSCTL_HANDLER_ARGS) | ||||
int fib = 0; | int fib = 0; | ||||
u_char af; | u_char af; | ||||
struct walkarg w; | struct walkarg w; | ||||
name ++; | name ++; | ||||
namelen--; | namelen--; | ||||
if (req->newptr) | if (req->newptr) | ||||
return (EPERM); | return (EPERM); | ||||
if (name[1] == NET_RT_DUMP) { | if (name[1] == NET_RT_DUMP || name[1] == NET_RT_NHOP || name[1] == NET_RT_NHGROUPS) { | ||||
if (namelen == 3) | if (namelen == 3) | ||||
fib = req->td->td_proc->p_fibnum; | fib = req->td->td_proc->p_fibnum; | ||||
else if (namelen == 4) | else if (namelen == 4) | ||||
fib = (name[3] == RT_ALL_FIBS) ? | fib = (name[3] == RT_ALL_FIBS) ? | ||||
req->td->td_proc->p_fibnum : name[3]; | req->td->td_proc->p_fibnum : name[3]; | ||||
else | else | ||||
return ((namelen < 3) ? EISDIR : ENOTDIR); | return ((namelen < 3) ? EISDIR : ENOTDIR); | ||||
if (fib < 0 || fib >= rt_numfibs) | if (fib < 0 || fib >= rt_numfibs) | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | for (error = 0; error == 0 && i <= lim; i++) { | ||||
RIB_RLOCK(rnh); | RIB_RLOCK(rnh); | ||||
error = rnh->rnh_walktree(&rnh->head, | error = rnh->rnh_walktree(&rnh->head, | ||||
sysctl_dumpentry, &w); | sysctl_dumpentry, &w); | ||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
} else if (af != 0) | } else if (af != 0) | ||||
error = EAFNOSUPPORT; | error = EAFNOSUPPORT; | ||||
} | } | ||||
break; | break; | ||||
case NET_RT_NHOP: | |||||
case NET_RT_NHGROUPS: | |||||
/* Allow dumping one specific af/fib at a time */ | |||||
if (namelen < 4) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
fib = name[3]; | |||||
if (fib < 0 || fib > rt_numfibs) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
rnh = rt_tables_get_rnh(fib, af); | |||||
if (rnh == NULL) { | |||||
error = EAFNOSUPPORT; | |||||
break; | |||||
} | |||||
if (w.w_op == NET_RT_NHOP) | |||||
error = nhops_dump_sysctl(rnh, w.w_req); | |||||
#ifdef ROUTE_MPATH | |||||
else | |||||
error = nhgrp_dump_sysctl(rnh, w.w_req); | |||||
#endif | |||||
break; | |||||
case NET_RT_IFLIST: | case NET_RT_IFLIST: | ||||
case NET_RT_IFLISTL: | case NET_RT_IFLISTL: | ||||
error = sysctl_iflist(af, &w); | error = sysctl_iflist(af, &w); | ||||
break; | break; | ||||
case NET_RT_IFMALIST: | case NET_RT_IFMALIST: | ||||
error = sysctl_ifmalist(af, &w); | error = sysctl_ifmalist(af, &w); | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | rt_dumpaddr_ddb(const char *name, const struct sockaddr *sa) | ||||
} | } | ||||
db_printf("%s <af:%d> ", name, sa->sa_family); | db_printf("%s <af:%d> ", name, sa->sa_family); | ||||
} | } | ||||
static int | static int | ||||
rt_dumpentry_ddb(struct radix_node *rn, void *arg __unused) | rt_dumpentry_ddb(struct radix_node *rn, void *arg __unused) | ||||
{ | { | ||||
struct sockaddr_storage ss; | struct sockaddr_in6 ss; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
struct nhop_object *nh; | |||||
int flags, idx; | int flags, idx; | ||||
/* If RNTORT is important, put it in a header. */ | /* If RNTORT is important, put it in a header. */ | ||||
rt = (void *)rn; | rt = (void *)rn; | ||||
/* XXX: mpath */ | |||||
nh = rt->rt_nhop; | |||||
rt_dumpaddr_ddb("dst", rt_key(rt)); | rt_dumpaddr_ddb("dst", rt_key(rt)); | ||||
rt_dumpaddr_ddb("gateway", rt->rt_gateway); | rt_dumpaddr_ddb("gateway", &nh->gw_sa); | ||||
rt_dumpaddr_ddb("netmask", rtsock_fix_netmask(rt_key(rt), rt_mask(rt), | rt_dumpaddr_ddb("netmask", rtsock_fix_netmask(rt_key(rt), rt_mask(rt), | ||||
&ss)); | &ss)); | ||||
if (rt->rt_ifp != NULL && (rt->rt_ifp->if_flags & IFF_DYING) == 0) { | if (nh->nh_ifp != NULL && (nh->nh_ifp->if_flags & IFF_DYING) == 0) { | ||||
rt_dumpaddr_ddb("ifp", rt->rt_ifp->if_addr->ifa_addr); | rt_dumpaddr_ddb("ifp", nh->nh_ifp->if_addr->ifa_addr); | ||||
rt_dumpaddr_ddb("ifa", rt->rt_ifa->ifa_addr); | rt_dumpaddr_ddb("ifa", nh->nh_ifa->ifa_addr); | ||||
} | } | ||||
db_printf("flags "); | db_printf("flags "); | ||||
flags = rt->rt_flags; | flags = rt->rte_flags; | ||||
if (flags == 0) | if (flags == 0) | ||||
db_printf("none"); | db_printf("none"); | ||||
while ((idx = ffs(flags)) > 0) { | while ((idx = ffs(flags)) > 0) { | ||||
idx--; | idx--; | ||||
if (flags != rt->rt_flags) | if (flags != rt->rte_flags) | ||||
db_printf(","); | db_printf(","); | ||||
db_printf("%s", rt_flag_name(idx)); | db_printf("%s", rt_flag_name(idx)); | ||||
flags &= ~(1ul << idx); | flags &= ~(1ul << idx); | ||||
} | } | ||||
db_printf("\n"); | db_printf("\n"); | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 266 Lines • ▼ Show 20 Lines | if (af == AF_INET) { | ||||
dstp = NULL; | dstp = NULL; | ||||
} | } | ||||
bp = inet_ntop(af, dst_addrp, buf, sizeof(buf)); | bp = inet_ntop(af, dst_addrp, buf, sizeof(buf)); | ||||
if (bp != NULL) | if (bp != NULL) | ||||
db_printf("Looking up route to destination '%s'\n", bp); | db_printf("Looking up route to destination '%s'\n", bp); | ||||
CURVNET_SET(vnet0); | CURVNET_SET(vnet0); | ||||
rt = rtalloc1(dstp, 0, RTF_RNH_LOCKED); | rt = rtalloc1_fib(dstp, 0, RTF_RNH_LOCKED, 0); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
if (rt == NULL) { | if (rt == NULL) { | ||||
db_printf("Could not get route for that server.\n"); | db_printf("Could not get route for that server.\n"); | ||||
return; | return; | ||||
} | } | ||||
rt_dumpentry_ddb((void *)rt, NULL); | rt_dumpentry_ddb((void *)rt, NULL); | ||||
Show All 11 Lines |