Page MenuHomeFreeBSD

D29826.diff
No OneTemporary

D29826.diff

diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -126,6 +126,13 @@
#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, ...) \
printf("rtsock:%s(): PID %d: " _fmt "\n", __func__, curproc->p_pid, ## __VA_ARGS__)
@@ -177,7 +184,7 @@
struct walkarg *w, int *plen);
static int rt_xaddrs(caddr_t cp, caddr_t cplim,
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_dumpnhop(struct rtentry *rt, struct nhop_object *nh,
uint32_t weight, struct walkarg *w);
@@ -621,7 +628,8 @@
* Returns 0 on success.
*/
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;
sa_family_t saf;
@@ -641,7 +649,7 @@
return (EINVAL);
info->rti_flags = rtm->rtm_flags;
- error = cleanup_xaddrs(info);
+ error = cleanup_xaddrs(info, lb);
if (error != 0)
return (error);
saf = info->rti_info[RTAX_DST]->sa_family;
@@ -878,6 +886,45 @@
#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.
@@ -891,11 +938,10 @@
update_rtm_from_rc(struct rt_addrinfo *info, struct rt_msghdr **prtm,
int alloc_len, struct rib_cmd_info *rc, struct nhop_object *nh)
{
- struct walkarg w;
union sockaddr_union saun;
- struct rt_msghdr *rtm, *orig_rtm = NULL;
+ struct rt_msghdr *rtm;
struct ifnet *ifp;
- int error, len;
+ int error;
rtm = *prtm;
union sockaddr_union sa_dst, sa_mask;
@@ -927,28 +973,8 @@
} else if (ifp != NULL)
rtm->rtm_index = ifp->if_index;
- /* Check if we need to realloc storage */
- rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len);
- if (len > alloc_len) {
- struct rt_msghdr *tmp_rtm;
-
- tmp_rtm = malloc(len, M_TEMP, M_NOWAIT);
- if (tmp_rtm == NULL)
- return (ENOBUFS);
- bcopy(rtm, tmp_rtm, rtm->rtm_msglen);
- orig_rtm = rtm;
- rtm = tmp_rtm;
- alloc_len = len;
-
- /*
- * Delay freeing original rtm as info contains
- * data referencing it.
- */
- }
-
- w.w_tmem = (caddr_t)rtm;
- w.w_tmemsize = alloc_len;
- rtsock_msg_buffer(rtm->rtm_type, info, &w, &len);
+ if ((error = update_rtm_from_info(info, prtm, alloc_len)) != 0)
+ return (error);
rtm->rtm_flags = rc->rc_rt->rte_flags | nhop_get_rtflags(nh);
if (rtm->rtm_flags & RTF_GWFLAG_COMPAT)
@@ -956,11 +982,6 @@
(rtm->rtm_flags & ~RTF_GWFLAG_COMPAT);
rt_getmetrics(rc->rc_rt, nh, &rtm->rtm_rmx);
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);
}
@@ -985,6 +1006,17 @@
}
#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*/
static int
route_output(struct mbuf *m, struct socket *so, ...)
@@ -1022,12 +1054,17 @@
* buffer aligned on 1k boundaty.
*/
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);
m_copydata(m, 0, len, (caddr_t)rtm);
bzero(&info, sizeof(info));
nh = NULL;
+ struct linear_buffer lb = {
+ .base = (char *)rtm + alloc_len,
+ .size = SCRATCH_BUFFER_SIZE,
+ };
if (rtm->rtm_version != RTM_VERSION) {
/* Do not touch message since format is unknown */
@@ -1042,19 +1079,19 @@
* 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);
}
+ /* fill_addringo() embeds scope into IPv6 addresses */
+#ifdef INET6
+ rti_need_deembed = 1;
+#endif
saf = info.rti_info[RTAX_DST]->sa_family;
/* support for new ARP code */
if (rtm->rtm_flags & RTF_LLDATA) {
error = lla_rt_output(rtm, &info);
-#ifdef INET6
- if (error == 0)
- rti_need_deembed = 1;
-#endif
goto flush;
}
@@ -1067,7 +1104,6 @@
error = EINVAL;
if (error != 0)
senderr(error);
- /* TODO: rebuild rtm from scratch */
}
switch (rtm->rtm_type) {
@@ -1079,9 +1115,6 @@
}
error = rib_action(fibnum, rtm->rtm_type, &info, &rc);
if (error == 0) {
-#ifdef INET6
- rti_need_deembed = 1;
-#endif
#ifdef ROUTE_MPATH
if (NH_IS_NHGRP(rc.rc_nh_new) ||
(rc.rc_nh_old && NH_IS_NHGRP(rc.rc_nh_old))) {
@@ -1110,12 +1143,7 @@
}
#endif
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;
case RTM_GET:
@@ -1124,13 +1152,18 @@
senderr(error);
nh = rc.rc_nh_new;
-report:
if (!can_export_rte(curthread->td_ucred,
info.rti_info[RTAX_NETMASK] == NULL,
info.rti_info[RTAX_DST])) {
senderr(ESRCH);
}
+ break;
+ default:
+ senderr(EOPNOTSUPP);
+ }
+
+ if (error == 0) {
error = update_rtm_from_rc(&info, &rtm, alloc_len, &rc, nh);
/*
* Note that some sockaddr pointers may have changed to
@@ -1147,12 +1180,6 @@
#ifdef INET6
rti_need_deembed = 0;
#endif
- if (error != 0)
- senderr(error);
- break;
-
- default:
- senderr(EOPNOTSUPP);
}
flush:
@@ -1174,6 +1201,10 @@
bcopy(sin6, info.rti_info[i],
sizeof(*sin6));
}
+ if (update_rtm_from_info(&info, &rtm, alloc_len) != 0) {
+ if (error != 0)
+ error = ENOBUFS;
+ }
}
}
#endif
@@ -1350,9 +1381,10 @@
}
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 *sa;
if (info->rti_flags & RTF_LLDATA)
return (cleanup_xaddrs_lladdr(info));
@@ -1362,11 +1394,17 @@
case AF_INET:
{
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);
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;
#endif
@@ -1392,13 +1430,17 @@
RTS_PID_PRINTF("gateway sdl_len too small: %d", gw_sdl->sdl_len);
return (EINVAL);
}
+ sa = alloc_sockaddr_aligned(lb, sizeof(struct sockaddr_dl_short));
+ if (sa == NULL)
+ return (ENOBUFS);
const struct sockaddr_dl_short sdl = {
.sdl_family = AF_LINK,
- .sdl_len = sdl_min_len,
+ .sdl_len = sizeof(struct sockaddr_dl_short),
.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;
}
}
@@ -1416,39 +1458,57 @@
#ifdef INET
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;
+ const int sa_len = sizeof(struct sockaddr_in);
+ struct in_addr dst, mask;
/* Check & fixup dst/netmask combination first */
dst_sa = (struct sockaddr_in *)info->rti_info[RTAX_DST];
mask_sa = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
- struct in_addr mask = {
- .s_addr = mask_sa ? mask_sa->sin_addr.s_addr : INADDR_BROADCAST,
- };
- 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");
+ /* Ensure reads do not go beyound the buffer size */
+ if (SA_SIZE(dst_sa) < offsetof(struct sockaddr_in, sin_zero))
return (EINVAL);
- }
- if (mask_sa && mask_sa->sin_len < sizeof(struct sockaddr_in)) {
- RTS_PID_PRINTF("prefix mask sin_len too small: %d", mask_sa->sin_len);
- return (EINVAL);
- }
+
+ 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);
+ 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);
+ 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);
- else
+ info->rti_info[RTAX_NETMASK] = (struct sockaddr *)mask_sa;
+ } else
remove_netmask(info);
/* Check gateway */
if (info->rti_info[RTAX_GATEWAY] != NULL)
- return (cleanup_xaddrs_gateway(info));
+ return (cleanup_xaddrs_gateway(info, lb));
return (0);
}
@@ -1456,43 +1516,66 @@
#ifdef INET6
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 in6_addr mask;
+ struct in6_addr mask, *dst;
+ const int sa_len = sizeof(struct sockaddr_in6);
/* Check & fixup dst/netmask combination first */
dst_sa = (struct sockaddr_in6 *)info->rti_info[RTAX_DST];
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)) {
RTS_PID_PRINTF("prefix dst sin6_len too small: %d", dst_sa->sin6_len);
return (EINVAL);
}
+
if (mask_sa && mask_sa->sin6_len < sizeof(struct sockaddr_in6)) {
- RTS_PID_PRINTF("rtsock: prefix mask sin6_len too small: %d", mask_sa->sin6_len);
- return (EINVAL);
- }
- fill_sockaddr_inet6(dst_sa, &dst_sa->sin6_addr, 0);
+ /*
+ * 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);
+ return (EINVAL);
+ }
+ } else
+ mask = mask_sa ? mask_sa->sin6_addr : in6mask128;
- if (!IN6_ARE_ADDR_EQUAL(&mask, &in6mask128))
- fill_sockaddr_inet6(mask_sa, &mask, 0);
- else
+ dst = &dst_sa->sin6_addr;
+ IN6_MASK_ADDR(dst, &mask);
+
+ 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);
/* Check gateway */
if (info->rti_info[RTAX_GATEWAY] != NULL)
- return (cleanup_xaddrs_gateway(info));
+ return (cleanup_xaddrs_gateway(info, lb));
return (0);
}
#endif
static int
-cleanup_xaddrs(struct rt_addrinfo *info)
+cleanup_xaddrs(struct rt_addrinfo *info, struct linear_buffer *lb)
{
int error = EAFNOSUPPORT;
@@ -1511,12 +1594,12 @@
switch (info->rti_info[RTAX_DST]->sa_family) {
#ifdef INET
case AF_INET:
- error = cleanup_xaddrs_inet(info);
+ error = cleanup_xaddrs_inet(info, lb);
break;
#endif
#ifdef INET6
case AF_INET6:
- error = cleanup_xaddrs_inet6(info);
+ error = cleanup_xaddrs_inet6(info, lb);
break;
#endif
}

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 9, 7:33 AM (21 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14553226
Default Alt Text
D29826.diff (13 KB)

Event Timeline