update ip_pcbopts() to use sockopt(9) API.

cleans up function and one small fix is that we now stop copying user
options to the mbuf when the _EOL is given, previously this function
would continue to copy options.
This commit is contained in:
plunky 2008-10-12 11:15:54 +00:00
parent aa5a0c9b51
commit d2fcfe2b55
2 changed files with 81 additions and 76 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip_output.c,v 1.199 2008/10/12 10:23:18 plunky Exp $ */
/* $NetBSD: ip_output.c,v 1.200 2008/10/12 11:15:54 plunky Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -91,7 +91,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.199 2008/10/12 10:23:18 plunky Exp $");
__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.200 2008/10/12 11:15:54 plunky Exp $");
#include "opt_pfil_hooks.h"
#include "opt_inet.h"
@ -1216,18 +1216,8 @@ ip_ctloutput(int op, struct socket *so, struct sockopt *sopt)
#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:
@ -1436,62 +1426,63 @@ ip_ctloutput(int op, struct socket *so, struct sockopt *sopt)
* 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
/*
* IP first-hop destination address will be stored before
* actual options; move other options back
* and clear it when none present.
*/
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));
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))
m = m_get(M_DONTWAIT, MT_SOOPTS);
if (m == NULL)
return (ENOBUFS);
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;
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
@ -1500,29 +1491,43 @@ ip_pcbopts(struct mbuf **pcbopt, struct mbuf *m)
* 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);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip_var.h,v 1.89 2008/08/16 21:51:44 plunky Exp $ */
/* $NetBSD: ip_var.h,v 1.90 2008/10/12 11:15:54 plunky Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
@ -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 *