/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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
* SUCH DAMAGE.
*
* @(#)rtsock.c 8.7 (Berkeley) 10/12/95
*/
#if defined(INET) || defined(INET6)
static int
route_get_sdl_index(struct rt_addrinfo *info, int *sdl_index)
{
struct rtentry *nrt;
int error;
error = rtrequest1(RTM_GET, info, &nrt);
if (error != 0)
return error;
/*
* nrt->rt_ifp->if_index may not be correct
* due to changing to ifplo0.
*/
*sdl_index = satosdl(nrt->rt_gateway)->sdl_index;
rt_unref(nrt);
/*
* Verify that the socket has the appropriate privilege; RTM_GET
* is the only operation the non-superuser is allowed.
*/
if (kauth_authorize_network(so->so_cred, KAUTH_NETWORK_ROUTE,
0, rtm, NULL, NULL) != 0)
senderr(EACCES);
/*
* route(8) passes a sockaddr truncated with prefixlen.
* The kernel doesn't expect such sockaddr and need to
* use a buffer that is big enough for the sockaddr expected
* (padded with 0's). We keep the original length of the sockaddr.
*/
if (info.rti_info[RTAX_NETMASK]) {
/*
* Use the family of RTAX_DST, because RTAX_NETMASK
* can have a zero family if it comes from the radix
* tree via rt_mask().
*/
socklen_t sa_len = sockaddr_getsize_by_family(
info.rti_info[RTAX_DST]->sa_family);
socklen_t masklen = sockaddr_getlen(
info.rti_info[RTAX_NETMASK]);
if (sa_len != 0 && sa_len > masklen) {
KASSERT(sa_len <= sizeof(netmask));
memcpy(&netmask, info.rti_info[RTAX_NETMASK], masklen);
memset((char *)&netmask + masklen, 0, sa_len - masklen);
info.rti_info[RTAX_NETMASK] = sstocsa(&netmask);
}
}
switch (rtm->rtm_type) {
case RTM_ADD:
if (info.rti_info[RTAX_GATEWAY] == NULL) {
senderr(EINVAL);
}
#if defined(INET) || defined(INET6)
/* support for new ARP/NDP code with keeping backcompat */
if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {
const struct sockaddr_dl *sdlp =
satocsdl(info.rti_info[RTAX_GATEWAY]);
/* Allow routing requests by interface index */
if (sdlp->sdl_nlen == 0 && sdlp->sdl_alen == 0
&& sdlp->sdl_slen == 0)
goto fallback;
/*
* Old arp binaries don't set the sdl_index
* so we have to complement it.
*/
int sdl_index = sdlp->sdl_index;
if (sdl_index == 0) {
error = route_get_sdl_index(&info, &sdl_index);
if (error != 0)
goto fallback;
} else if (
info.rti_info[RTAX_DST]->sa_family == AF_INET) {
/*
* XXX workaround for SIN_PROXY case; proxy arp
* entry should be in an interface that has
* a network route including the destination,
* not a local (link) route that may not be a
* desired place, for example a tap.
*/
const struct sockaddr_inarp *sina =
(const struct sockaddr_inarp *)
info.rti_info[RTAX_DST];
if (sina->sin_other & SIN_PROXY) {
error = route_get_sdl_index(&info,
&sdl_index);
if (error != 0)
goto fallback;
}
}
error = lla_rt_output(rtm->rtm_type, rtm->rtm_flags,
rtm->rtm_rmx.rmx_expire, &info, sdl_index);
break;
}
fallback:
#endif /* defined(INET) || defined(INET6) */
error = rtrequest1(rtm->rtm_type, &info, &saved_nrt);
if (error == 0) {
_rt_setmetrics(rtm->rtm_inits, rtm, saved_nrt);
rt_unref(saved_nrt);
}
break;
case RTM_DELETE:
#if defined(INET) || defined(INET6)
/* support for new ARP/NDP code */
if (info.rti_info[RTAX_GATEWAY] &&
(info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
(rtm->rtm_flags & RTF_LLDATA) != 0) {
const struct sockaddr_dl *sdlp =
satocsdl(info.rti_info[RTAX_GATEWAY]);
error = lla_rt_output(rtm->rtm_type, rtm->rtm_flags,
rtm->rtm_rmx.rmx_expire, &info, sdlp->sdl_index);
rtm->rtm_flags &= ~RTF_UP;
break;
}
#endif
error = rtrequest1(rtm->rtm_type, &info, &saved_nrt);
if (error != 0)
break;
case RTM_GET:
case RTM_CHANGE:
case RTM_LOCK:
/* XXX This will mask info.rti_info[RTAX_DST] with
* info.rti_info[RTAX_NETMASK] before
* searching. It did not used to do that. --dyoung
*/
rt = NULL;
error = rtrequest1(RTM_GET, &info, &rt);
if (error != 0)
senderr(error);
if (rtm->rtm_type != RTM_GET) {/* XXX: too grotty */
if (memcmp(info.rti_info[RTAX_DST], rt_getkey(rt),
info.rti_info[RTAX_DST]->sa_len) != 0)
senderr(ESRCH);
if (info.rti_info[RTAX_NETMASK] == NULL &&
rt_mask(rt) != NULL)
senderr(ETOOMANYREFS);
}
/*
* XXX if arp/ndp requests an L2 entry, we have to obtain
* it from lltable while for the route command we have to
* return a route as it is. How to distinguish them?
* For newer arp/ndp, RTF_LLDATA flag set by arp/ndp
* indicates an L2 entry is requested. For old arp/ndp
* binaries, we check RTF_UP flag is NOT set; it works
* by the fact that arp/ndp don't set it while the route
* command sets it.
*/
if (((rtm->rtm_flags & RTF_LLDATA) != 0 ||
(rtm->rtm_flags & RTF_UP) == 0) &&
rtm->rtm_type == RTM_GET &&
sockaddr_cmp(rt_getkey(rt), info.rti_info[RTAX_DST]) != 0) {
int ll_flags = 0;
route_get_sdl(rt->rt_ifp, info.rti_info[RTAX_DST], &sdl,
&ll_flags);
info.rti_info[RTAX_GATEWAY] = sstocsa(&sdl);
error = route_output_report(rt, &info, rtm, &new_rtm);
if (error)
senderr(error);
if (new_rtm != NULL) {
old_rtm = rtm;
rtm = new_rtm;
}
rtm->rtm_flags |= RTF_LLDATA;
rtm->rtm_flags &= ~RTF_CONNECTED;
rtm->rtm_flags |= (ll_flags & LLE_STATIC) ? RTF_STATIC : 0;
break;
}
case RTM_CHANGE:
#ifdef NET_MPSAFE
/*
* Release rt_so_mtx to avoid a deadlock with route_intr
* and also serialize updating routes to avoid another.
*/
if (rt_updating) {
/* Release to allow the updater to proceed */
rt_unref(rt);
rt = NULL;
}
while (rt_updating) {
error = cv_wait_sig(&rt_update_cv, rt_so_mtx);
if (error != 0)
goto flush;
}
if (rt == NULL) {
error = rtrequest1(RTM_GET, &info, &rt);
if (error != 0)
goto flush;
}
rt_updating = true;
mutex_exit(rt_so_mtx);
flush:
if (rtm) {
if (error)
rtm->rtm_errno = error;
else
rtm->rtm_flags |= RTF_DONE;
}
family = info.rti_info[RTAX_DST] ? info.rti_info[RTAX_DST]->sa_family :
0;
/* We cannot free old_rtm until we have stopped using the
* pointers in info, some of which may point to sockaddrs
* in old_rtm.
*/
if (old_rtm != NULL)
Free(old_rtm);
if (rt) {
if (do_rt_free) {
#ifdef NET_MPSAFE
/*
* Release rt_so_mtx to avoid a deadlock with
* route_intr.
*/
mutex_exit(rt_so_mtx);
rt_free(rt);
mutex_enter(rt_so_mtx);
#else
rt_free(rt);
#endif
} else
rt_unref(rt);
}
{
struct rawcb *rp = NULL;
/*
* Check to see if we don't want our own messages.
*/
if ((so->so_options & SO_USELOOPBACK) == 0) {
if (COMPATNAME(route_info).ri_cb.any_count <= 1) {
if (rtm)
Free(rtm);
m_freem(m);
goto out;
}
/* There is another listener, so construct message */
rp = sotorawcb(so);
}
if (rtm) {
m_copyback(m, 0, rtm->rtm_msglen, rtm);
if (m->m_pkthdr.len < rtm->rtm_msglen) {
m_freem(m);
m = NULL;
} else if (m->m_pkthdr.len > rtm->rtm_msglen)
m_adj(m, rtm->rtm_msglen - m->m_pkthdr.len);
Free(rtm);
}
if (rp)
rp->rcb_proto.sp_family = 0; /* Avoid us */
if (family)
proto.sp_protocol = family;
if (m)
raw_input(m, &proto, &COMPATNAME(route_info).ri_src,
&COMPATNAME(route_info).ri_dst, &rt_rawcb);
if (rp)
rp->rcb_proto.sp_family = PF_XROUTE;
}
out:
curlwp_bindx(bound);
return error;
}
for (i = 0; i < RTAX_MAX && cp < cplim; i++) {
if ((rtinfo->rti_addrs & (1 << i)) == 0)
continue;
rtinfo->rti_info[i] = sa = (const struct sockaddr *)cp;
RT_XADVANCE(cp, sa);
}
/*
* Check for extra addresses specified, except RTM_GET asking
* for interface info.
*/
if (rtmtype == RTM_GET) {
if (((rtinfo->rti_addrs &
(~((1 << RTAX_IFP) | (1 << RTAX_IFA)))) & (~0U << i)) != 0)
return 1;
} else if ((rtinfo->rti_addrs & (~0U << i)) != 0)
return 1;
/* Check for bad data length. */
if (cp != cplim) {
if (i == RTAX_NETMASK + 1 && sa != NULL &&
cp - RT_XROUNDUP(sa->sa_len) + sa->sa_len == cplim)
/*
* The last sockaddr was info.rti_info[RTAX_NETMASK].
* We accept this for now for the sake of old
* binaries or third party softwares.
*/
;
else
return 1;
}
return 0;
}
switch (type) {
case RTM_ODELADDR:
case RTM_ONEWADDR:
case RTM_OCHGADDR:
if (rtsock_iflist_70_hook.hooked)
return sizeof(struct ifa_msghdr70);
else {
#ifdef RTSOCK_DEBUG
printf("%s: unsupported RTM type %d\n", __func__, type);
#endif
return -1;
}
case RTM_DELADDR:
case RTM_NEWADDR:
case RTM_CHGADDR:
return sizeof(struct ifa_xmsghdr);
case RTM_OOIFINFO:
if (rtsock_iflist_14_hook.hooked)
return sizeof(struct if_msghdr14);
else {
#ifdef RTSOCK_DEBUG
printf("%s: unsupported RTM type RTM_OOIFINFO\n",
__func__);
#endif
return -1;
}
case RTM_OIFINFO:
if (rtsock_iflist_50_hook.hooked)
return sizeof(struct if_msghdr50);
else {
#ifdef RTSOCK_DEBUG
printf("%s: unsupported RTM type RTM_OIFINFO\n",
__func__);
#endif
return -1;
}
case RTM_IFINFO:
return sizeof(struct if_xmsghdr);
case RTM_IFANNOUNCE:
case RTM_IEEE80211:
return sizeof(struct if_xannouncemsghdr);
default:
return sizeof(struct rt_xmsghdr);
}
}
struct mbuf *
COMPATNAME(rt_msg1)(int type, struct rt_addrinfo *rtinfo, void *data, int datalen)
{
struct rt_xmsghdr *rtm;
struct mbuf *m;
int i;
const struct sockaddr *sa;
int len, dlen;
m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m == NULL)
return m;
MCLAIM(m, &COMPATNAME(routedomain).dom_mowner);
if ((len = rt_getlen(type)) == -1)
goto out;
if (len > MHLEN + MLEN)
panic("%s: message too long", __func__);
else if (len > MHLEN) {
m->m_next = m_get(M_DONTWAIT, MT_DATA);
if (m->m_next == NULL)
goto out;
MCLAIM(m->m_next, m->m_owner);
m->m_pkthdr.len = len;
m->m_len = MHLEN;
m->m_next->m_len = len - MHLEN;
} else {
m->m_pkthdr.len = m->m_len = len;
}
m_reset_rcvif(m);
m_copyback(m, 0, datalen, data);
if (len > datalen)
(void)memset(mtod(m, char *) + datalen, 0, len - datalen);
rtm = mtod(m, struct rt_xmsghdr *);
for (i = 0; i < RTAX_MAX; i++) {
if ((sa = rtinfo->rti_info[i]) == NULL)
continue;
rtinfo->rti_addrs |= (1 << i);
dlen = RT_XROUNDUP(sa->sa_len);
m_copyback(m, len, sa->sa_len, sa);
if (dlen != sa->sa_len) {
/*
* Up to 7 + 1 nul's since roundup is to
* sizeof(uint64_t) (8 bytes)
*/
m_copyback(m, len + sa->sa_len,
dlen - sa->sa_len, "\0\0\0\0\0\0\0");
}
len += dlen;
}
if (m->m_pkthdr.len != len)
goto out;
rtm->rtm_msglen = len;
rtm->rtm_version = RTM_XVERSION;
rtm->rtm_type = type;
return m;
out:
m_freem(m);
return NULL;
}
/*
* rt_msg2
*
* fills 'cp' or 'w'.w_tmem with the routing socket message and
* returns the length of the message in 'lenp'.
*
* if walkarg is 0, cp is expected to be 0 or a buffer large enough to hold
* the message
* otherwise walkarg's w_needed is updated and if the user buffer is
* specified and w_needed indicates space exists the information is copied
* into the temp space (w_tmem). w_tmem is [re]allocated if necessary,
* if the allocation fails ENOBUFS is returned.
*/
static int
rt_msg2(int type, struct rt_addrinfo *rtinfo, void *cpv, struct rt_walkarg *w,
int *lenp)
{
int i;
int len, dlen, second_time = 0;
char *cp0, *cp = cpv;
/*
* This routine is called to generate a message from the routing
* socket indicating that a redirect has occurred, a routing lookup
* has failed, or that a protocol has detected timeouts to a particular
* destination.
*/
void
COMPATNAME(rt_missmsg)(int type, const struct rt_addrinfo *rtinfo, int flags,
int error)
{
struct rt_xmsghdr rtm;
struct mbuf *m;
const struct sockaddr *sa = rtinfo->rti_info[RTAX_DST];
struct rt_addrinfo info = *rtinfo;
/*
* This routine is called to generate a message from the routing
* socket indicating that the status of a network interface has changed.
*/
void
COMPATNAME(rt_ifmsg)(struct ifnet *ifp)
{
struct if_xmsghdr ifm;
struct mbuf *m;
struct rt_addrinfo info;
/*
* This is called to generate messages from the routing socket
* indicating a network interface has had addresses associated with it.
* if we ever reverse the logic and replace messages TO the routing
* socket indicate a request to configure interfaces, then it will
* be unnecessary as the routing socket will automatically generate
* copies of it.
*/
static void
COMPATNAME(rt_addrmsg0)(int cmd, struct ifaddr *ifa, int error,
struct rtentry *rt, const struct sockaddr *src)
{
#define cmdpass(__cmd, __pass) (((__cmd) << 2) | (__pass))
struct rt_addrinfo info;
const struct sockaddr *sa;
int pass;
struct mbuf *m;
struct ifnet *ifp;
struct rt_xmsghdr rtm;
struct ifa_xmsghdr ifam;
int ncmd;
/*
* This is called to generate routing socket messages indicating
* network interface arrival and departure.
*/
void
COMPATNAME(rt_ifannouncemsg)(struct ifnet *ifp, int what)
{
struct mbuf *m;
struct rt_addrinfo info;
COMPATCALL(rt_ifannouncemsg, (ifp, what));
if (COMPATNAME(route_info).ri_cb.any_count == 0)
return;
m = rt_makeifannouncemsg(ifp, RTM_IFANNOUNCE, what, &info);
if (m == NULL)
return;
COMPATNAME(route_enqueue)(m, 0);
}
/*
* This is called to generate routing socket messages indicating
* IEEE80211 wireless events.
* XXX we piggyback on the RTM_IFANNOUNCE msg format in a clumsy way.
*/
void
COMPATNAME(rt_ieee80211msg)(struct ifnet *ifp, int what, void *data,
size_t data_len)
{
struct mbuf *m;
struct rt_addrinfo info;
COMPATCALL(rt_ieee80211msg, (ifp, what, data, data_len));
if (COMPATNAME(route_info).ri_cb.any_count == 0)
return;
m = rt_makeifannouncemsg(ifp, RTM_IEEE80211, what, &info);
if (m == NULL)
return;
/*
* Append the ieee80211 data. Try to stick it in the
* mbuf containing the ifannounce msg; otherwise allocate
* a new mbuf and append.
*
* NB: we assume m is a single mbuf.
*/
if (data_len > M_TRAILINGSPACE(m)) {
struct mbuf *n = m_get(M_NOWAIT, MT_DATA);
if (n == NULL) {
m_freem(m);
return;
}
(void)memcpy(mtod(n, void *), data, data_len);
n->m_len = data_len;
m->m_next = n;
} else if (data_len > 0) {
(void)memcpy(mtod(m, uint8_t *) + m->m_len, data, data_len);
m->m_len += data_len;
}
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len += data_len;
mtod(m, struct if_xannouncemsghdr *)->ifan_msglen += data_len;
COMPATNAME(route_enqueue)(m, 0);
}
/*
* Enqueue a message to the software interrupt routine.
*/
void
COMPATNAME(route_enqueue)(struct mbuf *m, int family)
{
struct route_info * const ri = &COMPATNAME(route_info);
int wasempty;