tech-net archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
ip_pcbopts() to sockopt
Hi,
proceeding with pushing the socket options code further down the stack,
I've been looking at ip_pcbopts() which handles creating a 'IP options'
mbuf from the user provided data. My patch is attached but its not so
clear, so I've included the actual updated function below.
I think its right, and traceroute (which uses IP_OPTIONS to set a single
source routing address) still works for what thats worth.
points to note
- _EOL and _NOP options are not required to be in the options list
according to RFC791 and could be ignored but we did include them before
so I left them in. (presumably if the user didn't want them in, she
would leave them out :)
- previously if there was an _EOL then any data after the end of that
would get left in the mbuf to be attached to outgoing packets I think.
Not sure if that was intended? I've stopped copying at _EOL ..
- no checking is done of [IPOPT_OFFSET] which contains the offset of
the 'next' address. the RFC says it has a minimum legal value
of 4 (IPOPT_MINOFF) but perhaps that doesn't matter?
is this ok to commit?
(I can't claim to have a great understanding of this code :)
iain
/*
* Set up IP options in pcb for insertion in output packets.
* Store in mbuf with pointer in pcbopt, adding pseudo-option
* with destination address if source routed.
*/
int
ip_pcbopts(struct mbuf **pcbopt, const struct sockopt *sopt)
{
struct mbuf *m;
const u_char *cp;
u_char *dp;
int cnt;
uint8_t optval, olen, offset;
/* turn off any old options */
if (*pcbopt)
(void)m_free(*pcbopt);
*pcbopt = NULL;
cp = sopt->sopt_data;
cnt = sopt->sopt_size;
if (cnt == 0)
return (0); /* Only turning off any previous options */
#ifndef __vax__
if (cnt % sizeof(int32_t))
return (EINVAL);
#endif
m = m_get(M_WAIT, MT_SOOPTS);
dp = mtod(m, u_char *);
memset(dp, 0, sizeof(struct in_addr));
dp += sizeof(struct in_addr);
m->m_len = sizeof(struct in_addr);
/*
* IP option list according to RFC791. Each option is of the form
*
* [optval] [olen] [(olen - 2) data bytes]
*
* we validate the list and copy options to an mbuf for prepending
* to data packets. The IP first-hop destination address will be
* stored before actual options and is zero if unset.
*/
while (cnt > 0) {
optval = cp[IPOPT_OPTVAL];
if (optval == IPOPT_EOL || optval == IPOPT_NOP) {
olen = 1;
} else {
if (cnt < IPOPT_OLEN + 1)
goto bad;
olen = cp[IPOPT_OLEN];
if (olen < IPOPT_OLEN + 1 || olen > cnt)
goto bad;
}
if (optval == IPOPT_LSRR || optval == IPOPT_SSRR) {
/*
* user process specifies route as:
* ->A->B->C->D
* D must be our final destination (but we can't
* check that since we may not have connected yet).
* A is first hop destination, which doesn't appear in
* actual IP option, but is stored before the options.
*/
if (olen < IPOPT_OFFSET + 1 + sizeof(struct in_addr))
goto bad;
offset = cp[IPOPT_OFFSET];
memcpy(mtod(m, u_char *), cp + IPOPT_OFFSET + 1,
sizeof(struct in_addr));
cp += sizeof(struct in_addr);
cnt -= sizeof(struct in_addr);
olen -= sizeof(struct in_addr);
if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct
in_addr))
goto bad;
memcpy(dp, cp, olen);
dp[IPOPT_OPTVAL] = optval;
dp[IPOPT_OLEN] = olen;
dp[IPOPT_OFFSET] = offset;
break;
} else {
if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct
in_addr))
goto bad;
memcpy(dp, cp, olen);
break;
}
dp += olen;
m->m_len += olen;
if (optval == IPOPT_EOL)
break;
cp += olen;
cnt -= olen;
}
*pcbopt = m;
return (0);
bad:
(void)m_free(m);
return (EINVAL);
}
--- /usr/src/sys/netinet/ip_output.c 2008-08-16 22:51:43.000000000 +0100
+++ ip_output.c 2008-08-16 22:53:07.000000000 +0100
@@ -1216,18 +1216,8 @@ ip_ctloutput(int op, struct socket *so,
#ifdef notyet
case IP_RETOPTS:
#endif
- {
- struct mbuf *m;
-
- m = sockopt_getmbuf(sopt);
- if (m == NULL) {
- error = ENOBUFS;
- break;
- }
-
- error = ip_pcbopts(&inp->inp_options, m);
+ error = ip_pcbopts(&inp->inp_options, sopt);
break;
- }
case IP_TOS:
case IP_TTL:
@@ -1430,62 +1420,60 @@ ip_ctloutput(int op, struct socket *so,
* with destination address if source routed.
*/
int
-ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
+ip_pcbopts(struct mbuf **pcbopt, const struct sockopt *sopt)
{
- int cnt, optlen;
- u_char *cp;
- u_char opt;
+ struct mbuf *m;
+ const u_char *cp;
+ u_char *dp;
+ int cnt;
+ uint8_t optval, olen, offset;
/* turn off any old options */
if (*pcbopt)
(void)m_free(*pcbopt);
- *pcbopt = 0;
- if (m == (struct mbuf *)0 || m->m_len == 0) {
- /*
- * Only turning off any previous options.
- */
- if (m)
- (void)m_free(m);
- return (0);
- }
+ *pcbopt = NULL;
+
+ cp = sopt->sopt_data;
+ cnt = sopt->sopt_size;
+
+ if (cnt == 0)
+ return (0); /* Only turning off any previous options */
#ifndef __vax__
- if (m->m_len % sizeof(int32_t))
- goto bad;
+ if (cnt % sizeof(int32_t))
+ return (EINVAL);
#endif
+
+ m = m_get(M_WAIT, MT_SOOPTS);
+ dp = mtod(m, u_char *);
+ memset(dp, 0, sizeof(struct in_addr));
+ dp += sizeof(struct in_addr);
+ m->m_len = sizeof(struct in_addr);
+
/*
- * IP first-hop destination address will be stored before
- * actual options; move other options back
- * and clear it when none present.
+ * IP option list according to RFC791. Each option is of the form
+ *
+ * [optval] [olen] [(olen - 2) data bytes]
+ *
+ * we validate the list and copy options to an mbuf for prepending
+ * to data packets. The IP first-hop destination address will be
+ * stored before actual options and is zero if unset.
*/
- if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN])
- goto bad;
- cnt = m->m_len;
- m->m_len += sizeof(struct in_addr);
- cp = mtod(m, u_char *) + sizeof(struct in_addr);
- memmove(cp, mtod(m, void *), (unsigned)cnt);
- bzero(mtod(m, void *), sizeof(struct in_addr));
+ while (cnt > 0) {
+ optval = cp[IPOPT_OPTVAL];
- for (; cnt > 0; cnt -= optlen, cp += optlen) {
- opt = cp[IPOPT_OPTVAL];
- if (opt == IPOPT_EOL)
- break;
- if (opt == IPOPT_NOP)
- optlen = 1;
- else {
- if (cnt < IPOPT_OLEN + sizeof(*cp))
+ if (optval == IPOPT_EOL || optval == IPOPT_NOP) {
+ olen = 1;
+ } else {
+ if (cnt < IPOPT_OLEN + 1)
goto bad;
- optlen = cp[IPOPT_OLEN];
- if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
+
+ olen = cp[IPOPT_OLEN];
+ if (olen < IPOPT_OLEN + 1 || olen > cnt)
goto bad;
}
- switch (opt) {
-
- default:
- break;
- case IPOPT_LSRR:
- case IPOPT_SSRR:
+ if (optval == IPOPT_LSRR || optval == IPOPT_SSRR) {
/*
* user process specifies route as:
* ->A->B->C->D
@@ -1494,29 +1482,43 @@ ip_pcbopts(struct mbuf **pcbopt, struct
* A is first hop destination, which doesn't appear in
* actual IP option, but is stored before the options.
*/
- if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
+ if (olen < IPOPT_OFFSET + 1 + sizeof(struct in_addr))
goto bad;
- m->m_len -= sizeof(struct in_addr);
- cnt -= sizeof(struct in_addr);
- optlen -= sizeof(struct in_addr);
- cp[IPOPT_OLEN] = optlen;
- /*
- * Move first hop before start of options.
- */
- bcopy((void *)&cp[IPOPT_OFFSET+1], mtod(m, void *),
+
+ offset = cp[IPOPT_OFFSET];
+ memcpy(mtod(m, u_char *), cp + IPOPT_OFFSET + 1,
sizeof(struct in_addr));
- /*
- * Then copy rest of options back
- * to close up the deleted entry.
- */
- (void)memmove(&cp[IPOPT_OFFSET+1],
- &cp[IPOPT_OFFSET+1] + sizeof(struct in_addr),
- (unsigned)cnt - (IPOPT_MINOFF - 1));
+
+ cp += sizeof(struct in_addr);
+ cnt -= sizeof(struct in_addr);
+ olen -= sizeof(struct in_addr);
+
+ if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct
in_addr))
+ goto bad;
+
+ memcpy(dp, cp, olen);
+ dp[IPOPT_OPTVAL] = optval;
+ dp[IPOPT_OLEN] = olen;
+ dp[IPOPT_OFFSET] = offset;
+ break;
+ } else {
+ if (m->m_len + olen > MAX_IPOPTLEN + sizeof(struct
in_addr))
+ goto bad;
+
+ memcpy(dp, cp, olen);
break;
}
+
+ dp += olen;
+ m->m_len += olen;
+
+ if (optval == IPOPT_EOL)
+ break;
+
+ cp += olen;
+ cnt -= olen;
}
- if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
- goto bad;
+
*pcbopt = m;
return (0);
--- /usr/src/sys/netinet/ip_var.h 2008-08-16 22:51:44.000000000 +0100
+++ ip_var.h 2008-08-16 22:53:12.000000000 +0100
@@ -214,7 +214,7 @@ int ip_optcopy(struct ip *, struct ip *
u_int ip_optlen(struct inpcb *);
int ip_output(struct mbuf *, ...);
int ip_fragment(struct mbuf *, struct ifnet *, u_long);
-int ip_pcbopts(struct mbuf **, struct mbuf *);
+int ip_pcbopts(struct mbuf **, const struct sockopt *);
struct mbuf *
ip_reass(struct ipqent *, struct ipq *, struct ipqhead *);
struct in_ifaddr *
Home |
Main Index |
Thread Index |
Old Index