Page MenuHomeFreeBSD

D12586.id34349.diff
No OneTemporary

D12586.id34349.diff

Index: sys/netinet/ip_output.c
===================================================================
--- sys/netinet/ip_output.c
+++ sys/netinet/ip_output.c
@@ -955,13 +955,25 @@
int
ip_ctloutput(struct socket *so, struct sockopt *sopt)
{
- struct inpcb *inp = sotoinpcb(so);
+ struct inpcb *inp;
+ struct mbuf *options;
int error, optval;
+ bool rlock = false;
+ bool wlock = false;
#ifdef RSS
uint32_t rss_bucket;
int retval;
#endif
+ SOCK_LOCK(so);
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(inp);
+ SOCK_UNLOCK(so);
+
error = optval = 0;
if (sopt->sopt_level != IPPROTO_IP) {
error = EINVAL;
@@ -971,33 +983,33 @@
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(inp);
+ wlock = true;
if ((so->so_options & SO_REUSEADDR) != 0)
inp->inp_flags2 |= INP_REUSEADDR;
else
inp->inp_flags2 &= ~INP_REUSEADDR;
- INP_WUNLOCK(inp);
error = 0;
break;
case SO_REUSEPORT:
INP_WLOCK(inp);
+ wlock = true;
if ((so->so_options & SO_REUSEPORT) != 0)
inp->inp_flags2 |= INP_REUSEPORT;
else
inp->inp_flags2 &= ~INP_REUSEPORT;
- INP_WUNLOCK(inp);
error = 0;
break;
case SO_SETFIB:
INP_WLOCK(inp);
+ wlock = true;
inp->inp_inc.inc_fibnum = so->so_fibnum;
- INP_WUNLOCK(inp);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT
INP_WLOCK(inp);
+ wlock = true;
inp->inp_flags2 |= INP_RATE_LIMIT_CHANGED;
- INP_WUNLOCK(inp);
error = 0;
#else
error = EOPNOTSUPP;
@@ -1007,6 +1019,10 @@
break;
}
}
+ if (!wlock)
+ INP_WLOCK(inp);
+ if (!in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
return (error);
}
@@ -1036,9 +1052,9 @@
break;
}
INP_WLOCK(inp);
+ wlock = true;
error = ip_pcbopts(inp, sopt->sopt_name, m);
- INP_WUNLOCK(inp);
- return (error);
+ break;
}
case IP_BINDANY:
@@ -1092,20 +1108,20 @@
#define OPTSET(bit) do { \
INP_WLOCK(inp); \
+ wlock = true; \
if (optval) \
inp->inp_flags |= bit; \
else \
inp->inp_flags &= ~bit; \
- INP_WUNLOCK(inp); \
} while (0)
#define OPTSET2(bit, val) do { \
INP_WLOCK(inp); \
+ wlock = true; \
if (val) \
inp->inp_flags2 |= bit; \
else \
inp->inp_flags2 &= ~bit; \
- INP_WUNLOCK(inp); \
} while (0)
case IP_RECVOPTS:
@@ -1200,6 +1216,7 @@
break;
INP_WLOCK(inp);
+ wlock = true;
switch (optval) {
case IP_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(INP_LOWPORT);
@@ -1220,13 +1237,16 @@
error = EINVAL;
break;
}
- INP_WUNLOCK(inp);
break;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv4)) {
+ INP_WLOCK(inp);
+ wlock = true;
error = IPSEC_PCBCTL(ipv4, inp, sopt);
+ if (error)
+ wlock = false;
break;
}
/* FALLTHROUGH */
@@ -1242,12 +1262,18 @@
switch (sopt->sopt_name) {
case IP_OPTIONS:
case IP_RETOPTS:
- if (inp->inp_options)
+ if (inp->inp_options) {
+ options = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK|M_ZERO);
+ INP_RLOCK(inp);
+ bcopy(inp->inp_options, options, min(inp->inp_options->m_len, sopt->sopt_valsize));
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
error = sooptcopyout(sopt,
- mtod(inp->inp_options,
+ mtod(options,
char *),
- inp->inp_options->m_len);
- else
+ options->m_len);
+ return (error);
+ } else
sopt->sopt_valsize = 0;
break;
@@ -1315,6 +1341,8 @@
break;
case IP_PORTRANGE:
+ INP_RLOCK(inp);
+ rlock = true;
if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
@@ -1346,6 +1374,8 @@
break;
#ifdef RSS
case IP_RSSBUCKETID:
+ INP_RLOCK(inp);
+ rlock = true;
retval = rss_hash2bucket(inp->inp_flowid,
inp->inp_flowtype,
&rss_bucket);
@@ -1362,8 +1392,12 @@
optval = OPTBIT2(INP_BINDMULTI);
break;
}
+ if (!rlock)
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
- break;
+ return (error);
/*
* Multicast socket options are processed by the in_mcast
@@ -1380,7 +1414,9 @@
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv4)) {
+ INP_WLOCK(inp);
error = IPSEC_PCBCTL(ipv4, inp, sopt);
+ INP_WUNLOCK(inp);
break;
}
/* FALLTHROUGH */
@@ -1392,6 +1428,14 @@
}
break;
}
+ if (wlock && !in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
+ else {
+ if (!rlock)
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
+ }
return (error);
}
Index: sys/netinet/raw_ip.c
===================================================================
--- sys/netinet/raw_ip.c
+++ sys/netinet/raw_ip.c
@@ -563,19 +563,30 @@
int
rip_ctloutput(struct socket *so, struct sockopt *sopt)
{
- struct inpcb *inp = sotoinpcb(so);
- int error, optval;
+ struct inpcb *inp;
+ int optval;
+ int error = 0;
+ bool wlock = false;
+
+ SOCK_LOCK(so);
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(inp);
+ SOCK_UNLOCK(so);
if (sopt->sopt_level != IPPROTO_IP) {
if ((sopt->sopt_level == SOL_SOCKET) &&
(sopt->sopt_name == SO_SETFIB)) {
inp->inp_inc.inc_fibnum = so->so_fibnum;
- return (0);
+ goto done;
}
- return (EINVAL);
+ error = EINVAL;
+ goto done;
}
- error = 0;
switch (sopt->sopt_dir) {
case SOPT_GET:
switch (sopt->sopt_name) {
@@ -619,7 +630,7 @@
case MRT_DEL_BW_UPCALL:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_mrouter_get ? ip_mrouter_get(so, sopt) :
EOPNOTSUPP;
break;
@@ -637,6 +648,8 @@
sizeof optval);
if (error)
break;
+ INP_WLOCK(inp);
+ wlock = true;
if (optval)
inp->inp_flags |= INP_HDRINCL;
else
@@ -673,14 +686,14 @@
case IP_RSVP_ON:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_init(so);
break;
case IP_RSVP_OFF:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_done();
break;
@@ -688,7 +701,7 @@
case IP_RSVP_VIF_OFF:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_vif ?
ip_rsvp_vif(so, sopt) : EINVAL;
break;
@@ -707,7 +720,7 @@
case MRT_DEL_BW_UPCALL:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_mrouter_set ? ip_mrouter_set(so, sopt) :
EOPNOTSUPP;
break;
@@ -719,6 +732,12 @@
break;
}
+done:
+ if (!wlock)
+ INP_WLOCK(inp);
+ if (!in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
+
return (error);
}
Index: sys/netinet6/ip6_output.c
===================================================================
--- sys/netinet6/ip6_output.c
+++ sys/netinet6/ip6_output.c
@@ -133,7 +133,7 @@
struct ucred *, int);
static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *);
-static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *);
+static int ip6_getpcbopt(struct inpcb *, int, struct sockopt *);
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *,
struct ucred *, int, int, int);
@@ -1400,11 +1400,13 @@
{
int optdatalen, uproto;
void *optdata;
- struct inpcb *in6p = sotoinpcb(so);
+ struct inpcb *in6p;
int error, optval;
int level, op, optname;
int optlen;
struct thread *td;
+ bool rlock = false;
+ bool wlock = false;
#ifdef RSS
uint32_t rss_bucket;
int retval;
@@ -1428,6 +1430,15 @@
optval = 0;
uproto = (int)so->so_proto->pr_protocol;
+ SOCK_LOCK(so);
+ in6p = sotoinpcb(so);
+ if (in6p == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(in6p);
+ SOCK_UNLOCK(so);
+
if (level != IPPROTO_IPV6) {
error = EINVAL;
@@ -1436,33 +1447,33 @@
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(in6p);
+ wlock = true;
if ((so->so_options & SO_REUSEADDR) != 0)
in6p->inp_flags2 |= INP_REUSEADDR;
else
in6p->inp_flags2 &= ~INP_REUSEADDR;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_REUSEPORT:
INP_WLOCK(in6p);
+ wlock = true;
if ((so->so_options & SO_REUSEPORT) != 0)
in6p->inp_flags2 |= INP_REUSEPORT;
else
in6p->inp_flags2 &= ~INP_REUSEPORT;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_SETFIB:
INP_WLOCK(in6p);
+ wlock = true;
in6p->inp_inc.inc_fibnum = so->so_fibnum;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT
INP_WLOCK(in6p);
+ wlock = true;
in6p->inp_flags2 |= INP_RATE_LIMIT_CHANGED;
- INP_WUNLOCK(in6p);
error = 0;
#else
error = EOPNOTSUPP;
@@ -1496,6 +1507,8 @@
error = soopt_mcopyin(sopt, m); /* XXX */
if (error != 0)
break;
+ INP_WLOCK(in6p);
+ wlock = true;
error = ip6_pcbopts(&in6p->in6p_outputopts,
m, so, sopt);
m_freem(m); /* XXX */
@@ -1567,6 +1580,8 @@
error = EINVAL;
else {
/* -1 = kernel default */
+ INP_WLOCK(in6p);
+ wlock = true;
in6p->in6p_hops = optval;
if ((in6p->inp_vflag &
INP_IPV4) != 0)
@@ -1575,42 +1590,58 @@
break;
#define OPTSET(bit) \
do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
if (optval) \
in6p->inp_flags |= (bit); \
else \
in6p->inp_flags &= ~(bit); \
- INP_WUNLOCK(in6p); \
} while (/*CONSTCOND*/ 0)
#define OPTSET2292(bit) \
do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
in6p->inp_flags |= IN6P_RFC2292; \
if (optval) \
in6p->inp_flags |= (bit); \
else \
in6p->inp_flags &= ~(bit); \
- INP_WUNLOCK(in6p); \
} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0)
#define OPTSET2(bit, val) do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
if (val) \
in6p->inp_flags2 |= bit; \
else \
in6p->inp_flags2 &= ~bit; \
- INP_WUNLOCK(in6p); \
} while (0)
#define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0)
+#define OPTSET2292_EXCLUSIVE(bit) \
+do { \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
+ if (OPTBIT(IN6P_RFC2292)) { \
+ error = EINVAL; \
+ } else { \
+ if (optval) \
+ in6p->inp_flags |= (bit); \
+ else \
+ in6p->inp_flags &= ~(bit); \
+ } \
+} while (/*CONSTCOND*/ 0)
case IPV6_RECVPKTINFO:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_PKTINFO);
+ OPTSET2292_EXCLUSIVE(IN6P_PKTINFO);
break;
case IPV6_HOPLIMIT:
@@ -1622,6 +1653,8 @@
error = EINVAL;
break;
}
+ INP_WLOCK(in6p);
+ wlock = true;
optp = &in6p->in6p_outputopts;
error = ip6_pcbopt(IPV6_HOPLIMIT,
(u_char *)&optval, sizeof(optval),
@@ -1631,48 +1664,23 @@
}
case IPV6_RECVHOPLIMIT:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_HOPLIMIT);
+ OPTSET2292_EXCLUSIVE(IN6P_HOPLIMIT);
break;
case IPV6_RECVHOPOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_HOPOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_HOPOPTS);
break;
case IPV6_RECVDSTOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_DSTOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_DSTOPTS);
break;
case IPV6_RECVRTHDRDSTOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_RTHDRDSTOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_RTHDRDSTOPTS);
break;
case IPV6_RECVRTHDR:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_RTHDR);
+ OPTSET2292_EXCLUSIVE(IN6P_RTHDR);
break;
case IPV6_RECVPATHMTU:
@@ -1702,12 +1710,15 @@
* available only prior to bind(2).
* see ipng mailing list, Jun 22 2001.
*/
+ INP_WLOCK(in6p);
+ wlock = true;
if (in6p->inp_lport ||
!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
error = EINVAL;
break;
}
OPTSET(IN6P_IPV6_V6ONLY);
+ /* optclear vflag */
if (optval)
in6p->inp_vflag &= ~INP_IPV4;
else
@@ -1715,11 +1726,7 @@
break;
case IPV6_RECVTCLASS:
/* cannot mix with RFC2292 XXX */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_TCLASS);
+ OPTSET2292_EXCLUSIVE(IN6P_TCLASS);
break;
case IPV6_AUTOFLOWLABEL:
OPTSET(IN6P_AUTOFLOWLABEL);
@@ -1739,8 +1746,8 @@
case IPV6_RSS_LISTEN_BUCKET:
if ((optval >= 0) &&
(optval < rss_getnumbuckets())) {
- in6p->inp_rss_listen_bucket = optval;
OPTSET2(INP_RSS_BUCKET_SET, 1);
+ in6p->inp_rss_listen_bucket = optval;
} else {
error = EINVAL;
}
@@ -1763,6 +1770,8 @@
break;
{
struct ip6_pktopts **optp;
+ INP_WLOCK(in6p);
+ wlock = true;
optp = &in6p->in6p_outputopts;
error = ip6_pcbopt(optname,
(u_char *)&optval, sizeof(optval),
@@ -1801,7 +1810,7 @@
error = priv_check(td,
PRIV_NETINET_SETHDROPTS);
if (error)
- return (error);
+ break;
}
OPTSET2292(IN6P_HOPOPTS);
break;
@@ -1810,7 +1819,7 @@
error = priv_check(td,
PRIV_NETINET_SETHDROPTS);
if (error)
- return (error);
+ break;
}
OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
break;
@@ -1832,12 +1841,6 @@
int optlen;
struct ip6_pktopts **optp;
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
-
/*
* We only ensure valsize is not too large
* here. Further validation will be done
@@ -1847,6 +1850,14 @@
sizeof(optbuf_storage), 0);
if (error)
break;
+
+ INP_WLOCK(in6p);
+ wlock = true;
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
optlen = sopt->sopt_valsize;
optbuf = optbuf_storage;
optp = &in6p->in6p_outputopts;
@@ -1879,6 +1890,7 @@
break;
INP_WLOCK(in6p);
+ wlock = true;
switch (optval) {
case IPV6_PORTRANGE_DEFAULT:
in6p->inp_flags &= ~(INP_LOWPORT);
@@ -1899,13 +1911,16 @@
error = EINVAL;
break;
}
- INP_WUNLOCK(in6p);
break;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv6)) {
+ INP_WLOCK(in6p);
+ wlock = true;
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
+ if (error)
+ wlock = false;
break;
}
/* FALLTHROUGH */
@@ -2037,6 +2052,8 @@
break;
#ifdef RSS
case IPV6_RSSBUCKETID:
+ INP_RLOCK(in6p);
+ rlock = true;
retval =
rss_hash2bucket(in6p->inp_flowid,
in6p->inp_flowtype,
@@ -2059,24 +2076,36 @@
}
if (error)
break;
+ if (!rlock)
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
error = sooptcopyout(sopt, &optval,
sizeof optval);
- break;
+ return (error);
case IPV6_PATHMTU:
{
u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo;
+ struct in6_addr addr;
- if (!(so->so_state & SS_ISCONNECTED))
- return (ENOTCONN);
+ if (!(so->so_state & SS_ISCONNECTED)) {
+ error = ENOTCONN;
+ break;
+ }
/*
* XXX: we dot not consider the case of source
* routing, or optional information to specify
* the outgoing interface.
+ * Copy faddr out of in6p to avoid holding lock
+ * on inp during route lookup.
*/
+ INP_RLOCK(in6p);
+ bcopy(&in6p->in6p_faddr, &addr, sizeof(addr));
+ INP_RUNLOCK(in6p);
error = ip6_getpmtu_ctl(so->so_fibnum,
- &in6p->in6p_faddr, &pmtu);
+ &addr, &pmtu);
if (error)
break;
if (pmtu > IPV6_MAXPACKET)
@@ -2126,8 +2155,7 @@
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
case IPV6_PREFER_TEMPADDR:
- error = ip6_getpcbopt(in6p->in6p_outputopts,
- optname, sopt);
+ error = ip6_getpcbopt(in6p, optname, sopt);
break;
case IPV6_MULTICAST_IF:
@@ -2140,7 +2168,9 @@
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv6)) {
+ INP_RLOCK(in6p);
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
+ INP_RUNLOCK(in6p);
break;
}
/* FALLTHROUGH */
@@ -2152,6 +2182,14 @@
break;
}
}
+ if (wlock && !in_pcbrele_wlocked(in6p))
+ INP_WUNLOCK(in6p);
+ else {
+ if (!rlock)
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
+ }
return (error);
}
@@ -2160,7 +2198,7 @@
{
int error = 0, optval, optlen;
const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
- struct inpcb *in6p = sotoinpcb(so);
+ struct inpcb *in6p;
int level, op, optname;
level = sopt->sopt_level;
@@ -2172,6 +2210,15 @@
return (EINVAL);
}
+ SOCK_LOCK(so);
+ in6p = sotoinpcb(so);
+ if (in6p == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(in6p);
+ SOCK_UNLOCK(so);
+
switch (optname) {
case IPV6_CHECKSUM:
/*
@@ -2223,6 +2270,10 @@
break;
}
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
+
return (error);
}
@@ -2238,6 +2289,8 @@
int error = 0;
struct thread *td = sopt->sopt_td;
+ INP_WLOCK_ASSERT(sotoinpcb(so));
+
/* turn off any old options. */
if (opt) {
#ifdef DIAGNOSTIC
@@ -2302,17 +2355,50 @@
return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto));
}
+#define GET_PKTOPT_VAR(field, lenexpr) do { \
+ if (pktopt && pktopt->field) { \
+ INP_RUNLOCK(in6p); \
+ optdata = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK); \
+ malloc_optdata = true; \
+ INP_RLOCK(in6p); \
+ if (in6p->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \
+ INP_RUNLOCK(in6p); \
+ free(optdata, M_TEMP); \
+ return (ECONNRESET); \
+ } \
+ pktopt = in6p->in6p_outputopts; \
+ if (pktopt && pktopt->field) { \
+ optdatalen = min(lenexpr, sopt->sopt_valsize); \
+ bcopy(&pktopt->field, optdata, optdatalen); \
+ } else { \
+ free(optdata, M_TEMP); \
+ optdata = NULL; \
+ malloc_optdata = false; \
+ } \
+ } \
+} while(0)
+
+#define GET_PKTOPT_EXT_HDR(field) GET_PKTOPT_VAR(field, \
+ (((struct ip6_ext *)pktopt->field)->ip6e_len + 1) << 3)
+
+#define GET_PKTOPT_SOCKADDR(field) GET_PKTOPT_VAR(field, \
+ pktopt->field->sa_len)
+
static int
-ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
+ip6_getpcbopt(struct inpcb *in6p, int optname, struct sockopt *sopt)
{
void *optdata = NULL;
+ bool malloc_optdata = false;
int optdatalen = 0;
- struct ip6_ext *ip6e;
int error = 0;
struct in6_pktinfo null_pktinfo;
int deftclass = 0, on;
int defminmtu = IP6PO_MINMTU_MCASTONLY;
int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
+ struct ip6_pktopts *pktopt;
+
+ INP_RLOCK(in6p);
+ pktopt = in6p->in6p_outputopts;
switch (optname) {
case IPV6_PKTINFO:
@@ -2329,50 +2415,29 @@
break;
case IPV6_TCLASS:
if (pktopt && pktopt->ip6po_tclass >= 0)
- optdata = (void *)&pktopt->ip6po_tclass;
- else
- optdata = (void *)&deftclass;
+ deftclass = pktopt->ip6po_tclass;
+ optdata = (void *)&deftclass;
optdatalen = sizeof(int);
break;
case IPV6_HOPOPTS:
- if (pktopt && pktopt->ip6po_hbh) {
- optdata = (void *)pktopt->ip6po_hbh;
- ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_hbh);
break;
case IPV6_RTHDR:
- if (pktopt && pktopt->ip6po_rthdr) {
- optdata = (void *)pktopt->ip6po_rthdr;
- ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_rthdr);
break;
case IPV6_RTHDRDSTOPTS:
- if (pktopt && pktopt->ip6po_dest1) {
- optdata = (void *)pktopt->ip6po_dest1;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_dest1);
break;
case IPV6_DSTOPTS:
- if (pktopt && pktopt->ip6po_dest2) {
- optdata = (void *)pktopt->ip6po_dest2;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_dest2);
break;
case IPV6_NEXTHOP:
- if (pktopt && pktopt->ip6po_nexthop) {
- optdata = (void *)pktopt->ip6po_nexthop;
- optdatalen = pktopt->ip6po_nexthop->sa_len;
- }
+ GET_PKTOPT_SOCKADDR(ip6po_nexthop);
break;
case IPV6_USE_MIN_MTU:
if (pktopt)
- optdata = (void *)&pktopt->ip6po_minmtu;
- else
- optdata = (void *)&defminmtu;
+ defminmtu = pktopt->ip6po_minmtu;
+ optdata = (void *)&defminmtu;
optdatalen = sizeof(int);
break;
case IPV6_DONTFRAG:
@@ -2385,19 +2450,22 @@
break;
case IPV6_PREFER_TEMPADDR:
if (pktopt)
- optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
- else
- optdata = (void *)&defpreftemp;
+ defpreftemp = pktopt->ip6po_prefer_tempaddr;
+ optdata = (void *)&defpreftemp;
optdatalen = sizeof(int);
break;
default: /* should not happen */
#ifdef DIAGNOSTIC
panic("ip6_getpcbopt: unexpected option\n");
#endif
+ INP_RUNLOCK(in6p);
return (ENOPROTOOPT);
}
+ INP_RUNLOCK(in6p);
error = sooptcopyout(sopt, optdata, optdatalen);
+ if (malloc_optdata)
+ free(optdata, M_TEMP);
return (error);
}
@@ -3082,6 +3150,8 @@
{
int len;
+ INP_WLOCK_ASSERT(in6p);
+
if (!in6p->in6p_outputopts)
return 0;
Index: sys/netipsec/ipsec_pcb.c
===================================================================
--- sys/netipsec/ipsec_pcb.c
+++ sys/netipsec/ipsec_pcb.c
@@ -276,6 +276,8 @@
struct secpolicy **spp, *newsp;
int error, flags;
+ INP_WLOCK_ASSERT(inp);
+
xpl = (struct sadb_x_policy *)request;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
@@ -332,7 +334,6 @@
return (EINVAL);
}
- INP_WLOCK(inp);
if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) {
spp = &inp->inp_sp->sp_in;
flags = INP_INBOUND_POLICY;
@@ -352,7 +353,6 @@
inp->inp_sp->flags |= flags;
KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp));
}
- INP_WUNLOCK(inp);
return (0);
}
@@ -365,7 +365,7 @@
xpl = (struct sadb_x_policy *)request;
- INP_RLOCK(inp);
+ INP_WLOCK_ASSERT(inp);
flags = inp->inp_sp->flags;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
@@ -378,7 +378,6 @@
flags &= INP_OUTBOUND_POLICY;
break;
default:
- INP_RUNLOCK(inp);
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
xpl->sadb_x_policy_dir));
return (EINVAL);
@@ -386,7 +385,6 @@
if (flags == 0) {
/* Return ENTRUST policy */
- INP_RUNLOCK(inp);
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST;
xpl->sadb_x_policy_id = 0;
@@ -400,7 +398,6 @@
("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags));
key_addref(sp);
- INP_RUNLOCK(inp);
error = key_sp2msg(sp, request, len);
key_freesp(&sp);
if (error == EINVAL)
@@ -421,30 +418,43 @@
size_t optlen;
int error;
- if (inp->inp_sp == NULL)
+ INP_WLOCK_ASSERT(inp);
+
+ if (inp->inp_sp == NULL) {
+ INP_WUNLOCK(inp);
return (ENOPROTOOPT);
+ }
/* Limit maximum request size to PAGE_SIZE */
optlen = sopt->sopt_valsize;
- if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE)
+ if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) {
+ INP_WUNLOCK(inp);
return (EINVAL);
+ }
optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT);
- if (optdata == NULL)
+ if (optdata == NULL) {
+ INP_WUNLOCK(inp);
return (ENOBUFS);
+ }
/*
* We need a hint from the user, what policy is requested - input
* or output? User should specify it in the buffer, even for
* setsockopt().
*/
+ INP_WUNLOCK(inp);
error = sooptcopyin(sopt, optdata, optlen, optlen);
+ INP_WLOCK(inp);
if (error == 0) {
- if (sopt->sopt_dir == SOPT_SET)
+ if (sopt->sopt_dir == SOPT_SET) {
error = ipsec_set_pcbpolicy(inp,
sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL,
optdata, optlen);
- else {
+ if (error != 0)
+ INP_WUNLOCK(inp);
+ } else {
error = ipsec_get_pcbpolicy(inp, optdata, &optlen);
+ INP_WUNLOCK(inp);
if (error == 0)
error = sooptcopyout(sopt, optdata, optlen);
}

File Metadata

Mime Type
text/plain
Expires
Sat, Jul 25, 1:50 PM (13 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35475498
Default Alt Text
D12586.id34349.diff (24 KB)

Event Timeline