summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2019-01-18 20:46:03 +0000
committerclaudio <claudio@openbsd.org>2019-01-18 20:46:03 +0000
commit8dfea2b7531a2c2fbb6fa81b2d9b74d620cdb2d3 (patch)
treeec5b3d24392d37ecd1defae62dca296530c8e4a6
parentwhen removing the currently active network from the join list, disconnect (diff)
downloadwireguard-openbsd-8dfea2b7531a2c2fbb6fa81b2d9b74d620cdb2d3.tar.xz
wireguard-openbsd-8dfea2b7531a2c2fbb6fa81b2d9b74d620cdb2d3.zip
Bring back the ip_pcbopts() refactor. Pad the option buffer and therefor
the mbuf to the next word length as it is required by the standard. Also use the correct offset from the input mbuf. OK visa@, input & OK bluhm@
-rw-r--r--sys/netinet/ip_output.c93
1 files changed, 54 insertions, 39 deletions
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 747e1e72b77..95c94ee4b94 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_output.c,v 1.352 2019/01/18 02:10:40 claudio Exp $ */
+/* $OpenBSD: ip_output.c,v 1.353 2019/01/18 20:46:03 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;
+ m_freem(*pcbopt);
+ *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 *);
+ memset(p, 0, sizeof (*p)); /* 0 = IPOPT_EOL, needed for padding */
+ 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));
+ cp = mtod(m, u_char *);
- 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 {
+ } 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,48 @@ 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))
+ /* pad options to next word, since p was zeroed just adjust off */
+ off = (off + sizeof(int32_t) - 1) & ~(sizeof(int32_t) - 1);
+ 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);
}