Changeset View
Changeset View
Standalone View
Standalone View
sys/net/rtsock.c
Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | |||||
#define SA_SIZE32(sa) \ | #define SA_SIZE32(sa) \ | ||||
( (((struct sockaddr *)(sa))->sa_len == 0) ? \ | ( (((struct sockaddr *)(sa))->sa_len == 0) ? \ | ||||
sizeof(int) : \ | sizeof(int) : \ | ||||
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(int) - 1) ) ) | 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(int) - 1) ) ) | ||||
#endif /* COMPAT_FREEBSD32 */ | #endif /* COMPAT_FREEBSD32 */ | ||||
struct linear_buffer { | |||||
char *base; /* Base allocated memory pointer */ | |||||
uint32_t offset; /* Currently used offset */ | |||||
uint32_t size; /* Total buffer size */ | |||||
}; | |||||
#define SCRATCH_BUFFER_SIZE 1024 | |||||
#define RTS_PID_PRINTF(_fmt, ...) \ | #define RTS_PID_PRINTF(_fmt, ...) \ | ||||
printf("rtsock:%s(): PID %d: " _fmt "\n", __func__, curproc->p_pid, ## __VA_ARGS__) | printf("rtsock:%s(): PID %d: " _fmt "\n", __func__, curproc->p_pid, ## __VA_ARGS__) | ||||
MALLOC_DEFINE(M_RTABLE, "routetbl", "routing tables"); | MALLOC_DEFINE(M_RTABLE, "routetbl", "routing tables"); | ||||
/* NB: these are not modified */ | /* NB: these are not modified */ | ||||
static struct sockaddr route_src = { 2, PF_ROUTE, }; | static struct sockaddr route_src = { 2, PF_ROUTE, }; | ||||
static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, }; | static struct sockaddr sa_zero = { sizeof(sa_zero), AF_INET, }; | ||||
Show All 35 Lines | |||||
}; | }; | ||||
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 cleanup_xaddrs(struct rt_addrinfo *info); | static int cleanup_xaddrs(struct rt_addrinfo *info, struct linear_buffer *lb); | ||||
static int sysctl_dumpentry(struct rtentry *rt, void *vw); | static int sysctl_dumpentry(struct rtentry *rt, void *vw); | ||||
static int sysctl_dumpnhop(struct rtentry *rt, struct nhop_object *nh, | static int sysctl_dumpnhop(struct rtentry *rt, struct nhop_object *nh, | ||||
uint32_t weight, struct walkarg *w); | uint32_t weight, struct walkarg *w); | ||||
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, | static void rt_getmetrics(const struct rtentry *rt, | ||||
const struct nhop_object *nh, struct rt_metrics *out); | const struct nhop_object *nh, struct rt_metrics *out); | ||||
▲ Show 20 Lines • Show All 427 Lines • ▼ Show 20 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, struct linear_buffer *lb, u_int fibnum, | ||||
struct rt_addrinfo *info) | |||||
{ | { | ||||
int error; | int error; | ||||
sa_family_t saf; | sa_family_t saf; | ||||
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); | ||||
info->rti_flags = rtm->rtm_flags; | info->rti_flags = rtm->rtm_flags; | ||||
error = cleanup_xaddrs(info); | error = cleanup_xaddrs(info, lb); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
saf = info->rti_info[RTAX_DST]->sa_family; | 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) { | ||||
▲ Show 20 Lines • Show All 220 Lines • ▼ Show 20 Lines | if (dst->sa_family == AF_INET6) { | ||||
rt_get_inet6_prefix_pmask(rt, &dst6->sin6_addr, | rt_get_inet6_prefix_pmask(rt, &dst6->sin6_addr, | ||||
&mask6->sin6_addr, &scopeid); | &mask6->sin6_addr, &scopeid); | ||||
dst6->sin6_scope_id = scopeid; | dst6->sin6_scope_id = scopeid; | ||||
return; | return; | ||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
static int | |||||
update_rtm_from_info(struct rt_addrinfo *info, struct rt_msghdr **prtm, | |||||
int alloc_len) | |||||
{ | |||||
struct rt_msghdr *rtm, *orig_rtm = NULL; | |||||
struct walkarg w; | |||||
int len; | |||||
rtm = *prtm; | |||||
/* 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); | |||||
rtm->rtm_addrs = info->rti_addrs; | |||||
if (orig_rtm != NULL) | |||||
free(orig_rtm, M_TEMP); | |||||
*prtm = rtm; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Update sockaddrs, flags, etc in @prtm based on @rc data. | * Update sockaddrs, flags, etc in @prtm based on @rc data. | ||||
* rtm can be reallocated. | * 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_rc(struct rt_addrinfo *info, struct rt_msghdr **prtm, | update_rtm_from_rc(struct rt_addrinfo *info, struct rt_msghdr **prtm, | ||||
int alloc_len, struct rib_cmd_info *rc, struct nhop_object *nh) | int alloc_len, struct rib_cmd_info *rc, struct nhop_object *nh) | ||||
{ | { | ||||
struct walkarg w; | |||||
union sockaddr_union saun; | union sockaddr_union saun; | ||||
struct rt_msghdr *rtm, *orig_rtm = NULL; | struct rt_msghdr *rtm; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error, len; | int error; | ||||
rtm = *prtm; | rtm = *prtm; | ||||
union sockaddr_union sa_dst, sa_mask; | union sockaddr_union sa_dst, sa_mask; | ||||
int family = info->rti_info[RTAX_DST]->sa_family; | int family = info->rti_info[RTAX_DST]->sa_family; | ||||
init_sockaddrs_family(family, &sa_dst.sa, &sa_mask.sa); | init_sockaddrs_family(family, &sa_dst.sa, &sa_mask.sa); | ||||
export_rtaddrs(rc->rc_rt, &sa_dst.sa, &sa_mask.sa); | export_rtaddrs(rc->rc_rt, &sa_dst.sa, &sa_mask.sa); | ||||
info->rti_info[RTAX_DST] = &sa_dst.sa; | info->rti_info[RTAX_DST] = &sa_dst.sa; | ||||
Show All 15 Lines | if (ifp) { | ||||
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; | ||||
/* Check if we need to realloc storage */ | if ((error = update_rtm_from_info(info, prtm, alloc_len)) != 0) | ||||
rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len); | return (error); | ||||
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); | |||||
rtm->rtm_flags = rc->rc_rt->rte_flags | nhop_get_rtflags(nh); | rtm->rtm_flags = rc->rc_rt->rte_flags | nhop_get_rtflags(nh); | ||||
if (rtm->rtm_flags & RTF_GWFLAG_COMPAT) | if (rtm->rtm_flags & RTF_GWFLAG_COMPAT) | ||||
rtm->rtm_flags = RTF_GATEWAY | | rtm->rtm_flags = RTF_GATEWAY | | ||||
(rtm->rtm_flags & ~RTF_GWFLAG_COMPAT); | (rtm->rtm_flags & ~RTF_GWFLAG_COMPAT); | ||||
rt_getmetrics(rc->rc_rt, nh, &rtm->rtm_rmx); | rt_getmetrics(rc->rc_rt, nh, &rtm->rtm_rmx); | ||||
rtm->rtm_rmx.rmx_weight = rc->rc_nh_weight; | rtm->rtm_rmx.rmx_weight = rc->rc_nh_weight; | ||||
rtm->rtm_addrs = info->rti_addrs; | |||||
if (orig_rtm != NULL) | |||||
free(orig_rtm, M_TEMP); | |||||
*prtm = rtm; | |||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef ROUTE_MPATH | #ifdef ROUTE_MPATH | ||||
static void | static void | ||||
save_del_notification(struct rib_cmd_info *rc, void *_cbdata) | save_del_notification(struct rib_cmd_info *rc, void *_cbdata) | ||||
{ | { | ||||
struct rib_cmd_info *rc_new = (struct rib_cmd_info *)_cbdata; | struct rib_cmd_info *rc_new = (struct rib_cmd_info *)_cbdata; | ||||
if (rc->rc_cmd == RTM_DELETE) | if (rc->rc_cmd == RTM_DELETE) | ||||
*rc_new = *rc; | *rc_new = *rc; | ||||
} | } | ||||
static void | static void | ||||
save_add_notification(struct rib_cmd_info *rc, void *_cbdata) | save_add_notification(struct rib_cmd_info *rc, void *_cbdata) | ||||
{ | { | ||||
struct rib_cmd_info *rc_new = (struct rib_cmd_info *)_cbdata; | struct rib_cmd_info *rc_new = (struct rib_cmd_info *)_cbdata; | ||||
if (rc->rc_cmd == RTM_ADD) | if (rc->rc_cmd == RTM_ADD) | ||||
*rc_new = *rc; | *rc_new = *rc; | ||||
} | } | ||||
#endif | #endif | ||||
static struct sockaddr * | |||||
alloc_sockaddr_aligned(struct linear_buffer *lb, int len) | |||||
{ | |||||
len |= (sizeof(uint64_t) - 1); | |||||
if (lb->offset + len > lb->size) | |||||
return (NULL); | |||||
struct sockaddr *sa = (struct sockaddr *)(lb->base + lb->offset); | |||||
lb->offset += len; | |||||
return (sa); | |||||
} | |||||
/*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 rtentry *rt = NULL; | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
Show All 21 Lines | if (len < sizeof(*rtm) || | ||||
senderr(EINVAL); | senderr(EINVAL); | ||||
/* | /* | ||||
* Most of current messages are in range 200-240 bytes, | * Most of current messages are in range 200-240 bytes, | ||||
* minimize possible re-allocation on reply using larger size | * minimize possible re-allocation on reply using larger size | ||||
* buffer aligned on 1k boundaty. | * buffer aligned on 1k boundaty. | ||||
*/ | */ | ||||
alloc_len = roundup2(len, 1024); | alloc_len = roundup2(len, 1024); | ||||
if ((rtm = malloc(alloc_len, M_TEMP, M_NOWAIT)) == NULL) | int total_len = alloc_len + SCRATCH_BUFFER_SIZE; | ||||
if ((rtm = malloc(total_len, M_TEMP, M_NOWAIT)) == NULL) | |||||
senderr(ENOBUFS); | senderr(ENOBUFS); | ||||
m_copydata(m, 0, len, (caddr_t)rtm); | m_copydata(m, 0, len, (caddr_t)rtm); | ||||
bzero(&info, sizeof(info)); | bzero(&info, sizeof(info)); | ||||
nh = NULL; | nh = NULL; | ||||
struct linear_buffer lb = { | |||||
.base = (char *)rtm + alloc_len, | |||||
.size = SCRATCH_BUFFER_SIZE, | |||||
}; | |||||
if (rtm->rtm_version != RTM_VERSION) { | if (rtm->rtm_version != RTM_VERSION) { | ||||
/* Do not touch message since format is unknown */ | /* Do not touch message since format is unknown */ | ||||
free(rtm, M_TEMP); | free(rtm, M_TEMP); | ||||
rtm = NULL; | rtm = NULL; | ||||
senderr(EPROTONOSUPPORT); | senderr(EPROTONOSUPPORT); | ||||
} | } | ||||
/* | /* | ||||
* Starting from here, it is possible | * Starting from here, it is possible | ||||
* to alter original message and insert | * to alter original message and insert | ||||
* caller PID and error value. | * caller PID and error value. | ||||
*/ | */ | ||||
if ((error = fill_addrinfo(rtm, len, fibnum, &info)) != 0) { | if ((error = fill_addrinfo(rtm, len, &lb, fibnum, &info)) != 0) { | ||||
senderr(error); | senderr(error); | ||||
} | } | ||||
/* fill_addringo() embeds scope into IPv6 addresses */ | |||||
#ifdef INET6 | |||||
rti_need_deembed = 1; | |||||
#endif | |||||
saf = info.rti_info[RTAX_DST]->sa_family; | saf = info.rti_info[RTAX_DST]->sa_family; | ||||
/* support for new ARP code */ | /* support for new ARP code */ | ||||
if (rtm->rtm_flags & RTF_LLDATA) { | if (rtm->rtm_flags & RTF_LLDATA) { | ||||
error = lla_rt_output(rtm, &info); | error = lla_rt_output(rtm, &info); | ||||
#ifdef INET6 | |||||
if (error == 0) | |||||
rti_need_deembed = 1; | |||||
#endif | |||||
goto flush; | goto flush; | ||||
} | } | ||||
union sockaddr_union gw_saun; | union sockaddr_union gw_saun; | ||||
int blackhole_flags = rtm->rtm_flags & (RTF_BLACKHOLE|RTF_REJECT); | int blackhole_flags = rtm->rtm_flags & (RTF_BLACKHOLE|RTF_REJECT); | ||||
if (blackhole_flags != 0) { | if (blackhole_flags != 0) { | ||||
if (blackhole_flags != (RTF_BLACKHOLE | RTF_REJECT)) | if (blackhole_flags != (RTF_BLACKHOLE | RTF_REJECT)) | ||||
error = fill_blackholeinfo(&info, &gw_saun); | error = fill_blackholeinfo(&info, &gw_saun); | ||||
else | else | ||||
error = EINVAL; | error = EINVAL; | ||||
if (error != 0) | if (error != 0) | ||||
senderr(error); | senderr(error); | ||||
/* TODO: rebuild rtm from scratch */ | |||||
} | } | ||||
switch (rtm->rtm_type) { | 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_action(fibnum, rtm->rtm_type, &info, &rc); | error = rib_action(fibnum, rtm->rtm_type, &info, &rc); | ||||
if (error == 0) { | if (error == 0) { | ||||
#ifdef INET6 | |||||
rti_need_deembed = 1; | |||||
#endif | |||||
#ifdef ROUTE_MPATH | #ifdef ROUTE_MPATH | ||||
if (NH_IS_NHGRP(rc.rc_nh_new) || | if (NH_IS_NHGRP(rc.rc_nh_new) || | ||||
(rc.rc_nh_old && NH_IS_NHGRP(rc.rc_nh_old))) { | (rc.rc_nh_old && NH_IS_NHGRP(rc.rc_nh_old))) { | ||||
struct rib_cmd_info rc_simple = {}; | struct rib_cmd_info rc_simple = {}; | ||||
rib_decompose_notification(&rc, | rib_decompose_notification(&rc, | ||||
save_add_notification, (void *)&rc_simple); | save_add_notification, (void *)&rc_simple); | ||||
rc = rc_simple; | rc = rc_simple; | ||||
} | } | ||||
Show All 12 Lines | #ifdef ROUTE_MPATH | ||||
(rc.rc_nh_new && NH_IS_NHGRP(rc.rc_nh_new))) { | (rc.rc_nh_new && NH_IS_NHGRP(rc.rc_nh_new))) { | ||||
struct rib_cmd_info rc_simple = {}; | struct rib_cmd_info rc_simple = {}; | ||||
rib_decompose_notification(&rc, | rib_decompose_notification(&rc, | ||||
save_del_notification, (void *)&rc_simple); | save_del_notification, (void *)&rc_simple); | ||||
rc = rc_simple; | rc = rc_simple; | ||||
} | } | ||||
#endif | #endif | ||||
nh = rc.rc_nh_old; | nh = rc.rc_nh_old; | ||||
goto report; | |||||
} | } | ||||
#ifdef INET6 | |||||
/* rt_msg2() will not be used when RTM_DELETE fails. */ | |||||
rti_need_deembed = 1; | |||||
#endif | |||||
break; | break; | ||||
case RTM_GET: | case RTM_GET: | ||||
error = handle_rtm_get(&info, fibnum, rtm, &rc); | error = handle_rtm_get(&info, fibnum, rtm, &rc); | ||||
if (error != 0) | if (error != 0) | ||||
senderr(error); | senderr(error); | ||||
nh = rc.rc_nh_new; | nh = rc.rc_nh_new; | ||||
report: | |||||
if (!can_export_rte(curthread->td_ucred, | if (!can_export_rte(curthread->td_ucred, | ||||
info.rti_info[RTAX_NETMASK] == NULL, | info.rti_info[RTAX_NETMASK] == NULL, | ||||
info.rti_info[RTAX_DST])) { | info.rti_info[RTAX_DST])) { | ||||
senderr(ESRCH); | senderr(ESRCH); | ||||
} | } | ||||
break; | |||||
default: | |||||
senderr(EOPNOTSUPP); | |||||
} | |||||
if (error == 0) { | |||||
error = update_rtm_from_rc(&info, &rtm, alloc_len, &rc, nh); | error = update_rtm_from_rc(&info, &rtm, alloc_len, &rc, nh); | ||||
/* | /* | ||||
* 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 | ||||
if (error != 0) | |||||
senderr(error); | |||||
break; | |||||
default: | |||||
senderr(EOPNOTSUPP); | |||||
} | } | ||||
flush: | flush: | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
rt = NULL; | rt = NULL; | ||||
#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) | ||||
continue; | continue; | ||||
if (info.rti_info[i]->sa_family != AF_INET6) | if (info.rti_info[i]->sa_family != AF_INET6) | ||||
continue; | continue; | ||||
bcopy(info.rti_info[i], sin6, sizeof(*sin6)); | bcopy(info.rti_info[i], sin6, sizeof(*sin6)); | ||||
if (sa6_recoverscope(sin6) == 0) | if (sa6_recoverscope(sin6) == 0) | ||||
bcopy(sin6, info.rti_info[i], | bcopy(sin6, info.rti_info[i], | ||||
sizeof(*sin6)); | sizeof(*sin6)); | ||||
} | } | ||||
if (update_rtm_from_info(&info, &rtm, alloc_len) != 0) { | |||||
if (error != 0) | |||||
error = ENOBUFS; | |||||
} | } | ||||
} | } | ||||
} | |||||
#endif | #endif | ||||
send_rtm_reply(so, rtm, m, saf, fibnum, error); | send_rtm_reply(so, rtm, m, saf, fibnum, error); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Sends the prepared reply message in @rtm to all rtsock clients. | * Sends the prepared reply message in @rtm to all rtsock clients. | ||||
▲ Show 20 Lines • Show All 158 Lines • ▼ Show 20 Lines | cleanup_xaddrs_lladdr(struct rt_addrinfo *info) | ||||
if (offsetof(struct sockaddr_dl, sdl_data) + sdl->sdl_nlen + sdl->sdl_alen > sdl->sdl_len) | if (offsetof(struct sockaddr_dl, sdl_data) + sdl->sdl_nlen + sdl->sdl_alen > sdl->sdl_len) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
cleanup_xaddrs_gateway(struct rt_addrinfo *info) | cleanup_xaddrs_gateway(struct rt_addrinfo *info, struct linear_buffer *lb) | ||||
{ | { | ||||
struct sockaddr *gw = info->rti_info[RTAX_GATEWAY]; | struct sockaddr *gw = info->rti_info[RTAX_GATEWAY]; | ||||
struct sockaddr *sa; | |||||
if (info->rti_flags & RTF_LLDATA) | if (info->rti_flags & RTF_LLDATA) | ||||
return (cleanup_xaddrs_lladdr(info)); | return (cleanup_xaddrs_lladdr(info)); | ||||
switch (gw->sa_family) { | switch (gw->sa_family) { | ||||
#ifdef INET | #ifdef INET | ||||
case AF_INET: | case AF_INET: | ||||
{ | { | ||||
struct sockaddr_in *gw_sin = (struct sockaddr_in *)gw; | struct sockaddr_in *gw_sin = (struct sockaddr_in *)gw; | ||||
if (gw_sin->sin_len < sizeof(struct sockaddr_in)) { | |||||
/* Ensure reads do not go beyoud SA boundary */ | |||||
if (SA_SIZE(gw) < offsetof(struct sockaddr_in, sin_zero)) { | |||||
RTS_PID_PRINTF("gateway sin_len too small: %d", gw->sa_len); | RTS_PID_PRINTF("gateway sin_len too small: %d", gw->sa_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
fill_sockaddr_inet(gw_sin, gw_sin->sin_addr); | sa = alloc_sockaddr_aligned(lb, sizeof(struct sockaddr_in)); | ||||
if (sa == NULL) | |||||
return (ENOBUFS); | |||||
fill_sockaddr_inet((struct sockaddr_in *)sa, gw_sin->sin_addr); | |||||
info->rti_info[RTAX_GATEWAY] = sa; | |||||
} | } | ||||
break; | break; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
case AF_INET6: | case AF_INET6: | ||||
{ | { | ||||
struct sockaddr_in6 *gw_sin6 = (struct sockaddr_in6 *)gw; | struct sockaddr_in6 *gw_sin6 = (struct sockaddr_in6 *)gw; | ||||
if (gw_sin6->sin6_len < sizeof(struct sockaddr_in6)) { | if (gw_sin6->sin6_len < sizeof(struct sockaddr_in6)) { | ||||
Show All 9 Lines | case AF_LINK: | ||||
struct sockaddr_dl *gw_sdl; | struct sockaddr_dl *gw_sdl; | ||||
size_t sdl_min_len = offsetof(struct sockaddr_dl, sdl_data); | size_t sdl_min_len = offsetof(struct sockaddr_dl, sdl_data); | ||||
gw_sdl = (struct sockaddr_dl *)gw; | gw_sdl = (struct sockaddr_dl *)gw; | ||||
if (gw_sdl->sdl_len < sdl_min_len) { | if (gw_sdl->sdl_len < sdl_min_len) { | ||||
RTS_PID_PRINTF("gateway sdl_len too small: %d", gw_sdl->sdl_len); | RTS_PID_PRINTF("gateway sdl_len too small: %d", gw_sdl->sdl_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
sa = alloc_sockaddr_aligned(lb, sizeof(struct sockaddr_dl_short)); | |||||
if (sa == NULL) | |||||
return (ENOBUFS); | |||||
const struct sockaddr_dl_short sdl = { | const struct sockaddr_dl_short sdl = { | ||||
.sdl_family = AF_LINK, | .sdl_family = AF_LINK, | ||||
.sdl_len = sdl_min_len, | .sdl_len = sizeof(struct sockaddr_dl_short), | ||||
.sdl_index = gw_sdl->sdl_index, | .sdl_index = gw_sdl->sdl_index, | ||||
}; | }; | ||||
memcpy(gw_sdl, &sdl, sdl_min_len); | *((struct sockaddr_dl_short *)sa) = sdl; | ||||
info->rti_info[RTAX_GATEWAY] = sa; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
remove_netmask(struct rt_addrinfo *info) | remove_netmask(struct rt_addrinfo *info) | ||||
{ | { | ||||
info->rti_info[RTAX_NETMASK] = NULL; | info->rti_info[RTAX_NETMASK] = NULL; | ||||
info->rti_flags |= RTF_HOST; | info->rti_flags |= RTF_HOST; | ||||
info->rti_addrs &= ~RTA_NETMASK; | info->rti_addrs &= ~RTA_NETMASK; | ||||
} | } | ||||
#ifdef INET | #ifdef INET | ||||
static int | static int | ||||
cleanup_xaddrs_inet(struct rt_addrinfo *info) | cleanup_xaddrs_inet(struct rt_addrinfo *info, struct linear_buffer *lb) | ||||
{ | { | ||||
struct sockaddr_in *dst_sa, *mask_sa; | struct sockaddr_in *dst_sa, *mask_sa; | ||||
const int sa_len = sizeof(struct sockaddr_in); | |||||
struct in_addr dst, mask; | |||||
/* Check & fixup dst/netmask combination first */ | /* Check & fixup dst/netmask combination first */ | ||||
dst_sa = (struct sockaddr_in *)info->rti_info[RTAX_DST]; | dst_sa = (struct sockaddr_in *)info->rti_info[RTAX_DST]; | ||||
mask_sa = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK]; | mask_sa = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK]; | ||||
struct in_addr mask = { | /* Ensure reads do not go beyound the buffer size */ | ||||
.s_addr = mask_sa ? mask_sa->sin_addr.s_addr : INADDR_BROADCAST, | if (SA_SIZE(dst_sa) < offsetof(struct sockaddr_in, sin_zero)) | ||||
}; | |||||
struct in_addr dst = { | |||||
.s_addr = htonl(ntohl(dst_sa->sin_addr.s_addr) & ntohl(mask.s_addr)) | |||||
}; | |||||
if (dst_sa->sin_len < sizeof(struct sockaddr_in)) { | |||||
printf("dst sin_len too small\n"); | |||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
if (mask_sa && mask_sa->sin_len < sizeof(struct sockaddr_in)) { | if ((mask_sa != NULL) && mask_sa->sin_len < sizeof(struct sockaddr_in)) { | ||||
/* | |||||
* Some older routing software encode mask length into the | |||||
* sin_len, thus resulting in "truncated" sockaddr. | |||||
*/ | |||||
int len = mask_sa->sin_len - offsetof(struct sockaddr_in, sin_addr); | |||||
if (len >= 0) { | |||||
mask.s_addr = 0; | |||||
if (len > sizeof(struct in_addr)) | |||||
len = sizeof(struct in_addr); | |||||
memcpy(&mask, &mask_sa->sin_addr, len); | |||||
} else { | |||||
RTS_PID_PRINTF("prefix mask sin_len too small: %d", mask_sa->sin_len); | RTS_PID_PRINTF("prefix mask sin_len too small: %d", mask_sa->sin_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} else | |||||
mask.s_addr = mask_sa ? mask_sa->sin_addr.s_addr : INADDR_BROADCAST; | |||||
dst.s_addr = htonl(ntohl(dst_sa->sin_addr.s_addr) & ntohl(mask.s_addr)); | |||||
/* Construct new "clean" dst/mask sockaddresses */ | |||||
if ((dst_sa = (struct sockaddr_in *)alloc_sockaddr_aligned(lb, sa_len)) == NULL) | |||||
return (ENOBUFS); | |||||
fill_sockaddr_inet(dst_sa, dst); | fill_sockaddr_inet(dst_sa, dst); | ||||
info->rti_info[RTAX_DST] = (struct sockaddr *)dst_sa; | |||||
if (mask.s_addr != INADDR_BROADCAST) | if (mask.s_addr != INADDR_BROADCAST) { | ||||
if ((mask_sa = (struct sockaddr_in *)alloc_sockaddr_aligned(lb, sa_len)) == NULL) | |||||
return (ENOBUFS); | |||||
fill_sockaddr_inet(mask_sa, mask); | fill_sockaddr_inet(mask_sa, mask); | ||||
else | info->rti_info[RTAX_NETMASK] = (struct sockaddr *)mask_sa; | ||||
} else | |||||
remove_netmask(info); | remove_netmask(info); | ||||
/* Check gateway */ | /* Check gateway */ | ||||
if (info->rti_info[RTAX_GATEWAY] != NULL) | if (info->rti_info[RTAX_GATEWAY] != NULL) | ||||
return (cleanup_xaddrs_gateway(info)); | return (cleanup_xaddrs_gateway(info, lb)); | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
static int | static int | ||||
cleanup_xaddrs_inet6(struct rt_addrinfo *info) | cleanup_xaddrs_inet6(struct rt_addrinfo *info, struct linear_buffer *lb) | ||||
{ | { | ||||
struct sockaddr *sa; | |||||
struct sockaddr_in6 *dst_sa, *mask_sa; | struct sockaddr_in6 *dst_sa, *mask_sa; | ||||
struct in6_addr mask; | struct in6_addr mask, *dst; | ||||
const int sa_len = sizeof(struct sockaddr_in6); | |||||
/* Check & fixup dst/netmask combination first */ | /* Check & fixup dst/netmask combination first */ | ||||
dst_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_DST]; | dst_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_DST]; | ||||
mask_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK]; | mask_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK]; | ||||
mask = mask_sa ? mask_sa->sin6_addr : in6mask128; | |||||
IN6_MASK_ADDR(&dst_sa->sin6_addr, &mask); | |||||
if (dst_sa->sin6_len < sizeof(struct sockaddr_in6)) { | if (dst_sa->sin6_len < sizeof(struct sockaddr_in6)) { | ||||
RTS_PID_PRINTF("prefix dst sin6_len too small: %d", dst_sa->sin6_len); | RTS_PID_PRINTF("prefix dst sin6_len too small: %d", dst_sa->sin6_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (mask_sa && mask_sa->sin6_len < sizeof(struct sockaddr_in6)) { | if (mask_sa && mask_sa->sin6_len < sizeof(struct sockaddr_in6)) { | ||||
/* | |||||
* Some older routing software encode mask length into the | |||||
* sin6_len, thus resulting in "truncated" sockaddr. | |||||
*/ | |||||
int len = mask_sa->sin6_len - offsetof(struct sockaddr_in6, sin6_addr); | |||||
if (len >= 0) { | |||||
bzero(&mask, sizeof(mask)); | |||||
if (len > sizeof(struct in6_addr)) | |||||
len = sizeof(struct in6_addr); | |||||
memcpy(&mask, &mask_sa->sin6_addr, len); | |||||
} else { | |||||
RTS_PID_PRINTF("rtsock: prefix mask sin6_len too small: %d", mask_sa->sin6_len); | RTS_PID_PRINTF("rtsock: prefix mask sin6_len too small: %d", mask_sa->sin6_len); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
fill_sockaddr_inet6(dst_sa, &dst_sa->sin6_addr, 0); | } else | ||||
mask = mask_sa ? mask_sa->sin6_addr : in6mask128; | |||||
if (!IN6_ARE_ADDR_EQUAL(&mask, &in6mask128)) | dst = &dst_sa->sin6_addr; | ||||
fill_sockaddr_inet6(mask_sa, &mask, 0); | IN6_MASK_ADDR(dst, &mask); | ||||
else | |||||
if ((sa = alloc_sockaddr_aligned(lb, sa_len)) == NULL) | |||||
return (ENOBUFS); | |||||
fill_sockaddr_inet6((struct sockaddr_in6 *)sa, dst, 0); | |||||
info->rti_info[RTAX_DST] = sa; | |||||
if (!IN6_ARE_ADDR_EQUAL(&mask, &in6mask128)) { | |||||
if ((sa = alloc_sockaddr_aligned(lb, sa_len)) == NULL) | |||||
return (ENOBUFS); | |||||
fill_sockaddr_inet6((struct sockaddr_in6 *)sa, &mask, 0); | |||||
info->rti_info[RTAX_NETMASK] = sa; | |||||
} else | |||||
remove_netmask(info); | remove_netmask(info); | ||||
/* Check gateway */ | /* Check gateway */ | ||||
if (info->rti_info[RTAX_GATEWAY] != NULL) | if (info->rti_info[RTAX_GATEWAY] != NULL) | ||||
return (cleanup_xaddrs_gateway(info)); | return (cleanup_xaddrs_gateway(info, lb)); | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
static int | static int | ||||
cleanup_xaddrs(struct rt_addrinfo *info) | cleanup_xaddrs(struct rt_addrinfo *info, struct linear_buffer *lb) | ||||
{ | { | ||||
int error = EAFNOSUPPORT; | int error = EAFNOSUPPORT; | ||||
if (info->rti_info[RTAX_DST] == NULL) | if (info->rti_info[RTAX_DST] == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (info->rti_flags & RTF_LLDATA) { | if (info->rti_flags & RTF_LLDATA) { | ||||
/* | /* | ||||
* arp(8)/ndp(8) sends RTA_NETMASK for the associated | * arp(8)/ndp(8) sends RTA_NETMASK for the associated | ||||
* prefix along with the actual address in RTA_DST. | * prefix along with the actual address in RTA_DST. | ||||
* Remove netmask to avoid unnecessary address masking. | * Remove netmask to avoid unnecessary address masking. | ||||
*/ | */ | ||||
remove_netmask(info); | remove_netmask(info); | ||||
} | } | ||||
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: | ||||
error = cleanup_xaddrs_inet(info); | error = cleanup_xaddrs_inet(info, lb); | ||||
break; | break; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
case AF_INET6: | case AF_INET6: | ||||
error = cleanup_xaddrs_inet6(info); | error = cleanup_xaddrs_inet6(info, lb); | ||||
break; | break; | ||||
#endif | #endif | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 1,090 Lines • Show Last 20 Lines |