summaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2019-01-06 11:05:10 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2019-01-06 11:05:10 +0000
commitd85710f6342b7a3ba89add222a8b7e2163f1b4b0 (patch)
treeb59af2f580f25c271110f1123265b057db6c8479 /sys/netinet
parenta7e276d2dd22a043f79e6c0a53b1d01549d585ac (diff)
Rewrite ip_pcbopts() to fill a fresh mbuf with the ip options instead
of fiddling with the user supplied mbuf and then copy it at the end. OK visa@
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/ip_output.c85
1 files changed, 49 insertions, 36 deletions
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index ea9b1e98feb..e9c64fd2c75 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_output.c,v 1.350 2019/01/03 08:46:40 claudio Exp $ */
+/* $OpenBSD: ip_output.c,v 1.351 2019/01/06 11:05:09 claudio Exp $ */
/* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */
/*
@@ -1234,13 +1234,15 @@ ip_ctloutput(int op, struct socket *so, int level, int optname,
int
ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
{
- int cnt, optlen;
+ struct mbuf *n;
+ struct ipoption *p;
+ int cnt, off, optlen;
u_char *cp;
u_char opt;
/* turn off any old options */
m_free(*pcbopt);
- *pcbopt = 0;
+ *pcbopt = NULL;
if (m == NULL || m->m_len == 0) {
/*
* Only turning off any previous options.
@@ -1248,38 +1250,36 @@ ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
return (0);
}
- if (m->m_len % sizeof(int32_t))
+ if (m->m_len % sizeof(int32_t) ||
+ m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
return (EINVAL);
- /*
- * IP first-hop destination address will be stored before
- * actual options; move other options back
- * and clear it when none present.
- */
- if (m_trailingspace(m) < sizeof(struct in_addr))
- return (EINVAL);
+ /* Don't sleep because NET_LOCK() is hold. */
+ if ((n = m_get(M_NOWAIT, MT_SOOPTS)) == NULL)
+ return (ENOBUFS);
+ p = mtod(n, struct ipoption *);
+ p->ipopt_dst.s_addr = 0;
+ n->m_len = sizeof(struct in_addr);
+
+ off = 0;
cnt = m->m_len;
- m->m_len += sizeof(struct in_addr);
cp = mtod(m, u_char *) + sizeof(struct in_addr);
- memmove((caddr_t)cp, mtod(m, caddr_t), (unsigned)cnt);
- memset(mtod(m, caddr_t), 0, sizeof(struct in_addr));
- for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ while (cnt > 0) {
opt = cp[IPOPT_OPTVAL];
- if (opt == IPOPT_EOL)
- break;
- if (opt == IPOPT_NOP)
+
+ if (opt == IPOPT_NOP || opt == IPOPT_EOL)
optlen = 1;
else {
if (cnt < IPOPT_OLEN + sizeof(*cp))
- return (EINVAL);
+ goto bad;
optlen = cp[IPOPT_OLEN];
if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
- return (EINVAL);
+ goto bad;
}
switch (opt) {
-
default:
+ memcpy(p->ipopt_list + off, cp, optlen);
break;
case IPOPT_LSRR:
@@ -1293,33 +1293,46 @@ ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
* actual IP option, but is stored before the options.
*/
if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
- return (EINVAL);
- m->m_len -= sizeof(struct in_addr);
- cnt -= sizeof(struct in_addr);
+ goto bad;
+
+ /*
+ * Optlen is smaller because first address is popped.
+ * Cnt and cp will be adjusted a bit later to reflect
+ * this.
+ */
optlen -= sizeof(struct in_addr);
- cp[IPOPT_OLEN] = optlen;
+ p->ipopt_list[off + IPOPT_OPTVAL] = opt;
+ p->ipopt_list[off + IPOPT_OLEN] = optlen;
+
/*
* Move first hop before start of options.
*/
- memcpy(mtod(m, caddr_t), &cp[IPOPT_OFFSET+1],
+ memcpy(&p->ipopt_dst, cp + IPOPT_OFFSET,
sizeof(struct in_addr));
+ cp += sizeof(struct in_addr);
+ cnt -= sizeof(struct in_addr);
/*
- * Then copy rest of options back
- * to close up the deleted entry.
+ * Then copy rest of options
*/
- memmove((caddr_t)&cp[IPOPT_OFFSET+1],
- (caddr_t)(&cp[IPOPT_OFFSET+1] +
- sizeof(struct in_addr)),
- (unsigned)cnt - (IPOPT_OFFSET+1));
+ memcpy(p->ipopt_list + off + IPOPT_OFFSET,
+ cp + IPOPT_OFFSET, optlen - IPOPT_OFFSET);
break;
}
+ off += optlen;
+ cp += optlen;
+ cnt -= optlen;
+
+ if (opt == IPOPT_EOL)
+ break;
}
- if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
+ n->m_len += off;
+ if (n->m_len > sizeof(*p)) {
+ bad:
+ m_freem(n);
return (EINVAL);
- *pcbopt = m_copym(m, 0, M_COPYALL, M_NOWAIT);
- if (*pcbopt == NULL)
- return (ENOBUFS);
+ }
+ *pcbopt = n;
return (0);
}