Index: sys/netinet/ip_output.c =================================================================== --- sys/netinet/ip_output.c +++ sys/netinet/ip_output.c @@ -1076,17 +1076,23 @@ switch (sopt->sopt_name) { case IP_TOS: + INP_WLOCK(inp); inp->inp_ip_tos = optval; + INP_WUNLOCK(inp); break; case IP_TTL: + INP_WLOCK(inp); inp->inp_ip_ttl = optval; + INP_WUNLOCK(inp); break; case IP_MINTTL: - if (optval >= 0 && optval <= MAXTTL) + if (optval >= 0 && optval <= MAXTTL) { + INP_WLOCK(inp); inp->inp_ip_minttl = optval; - else + INP_WUNLOCK(inp); + } else error = EINVAL; break; @@ -1099,12 +1105,15 @@ INP_WUNLOCK(inp); \ } while (0) -#define OPTSET2(bit, val) do { \ - INP_WLOCK(inp); \ +#define OPTSET2_N(bit, val) do { \ if (val) \ inp->inp_flags2 |= bit; \ else \ inp->inp_flags2 &= ~bit; \ +} while (0) +#define OPTSET2(bit, val) do { \ + INP_WLOCK(inp); \ + OPTSET2_N(bit, val); \ INP_WUNLOCK(inp); \ } while (0) @@ -1154,8 +1163,10 @@ case IP_RSS_LISTEN_BUCKET: if ((optval >= 0) && (optval < rss_getnumbuckets())) { + INP_WLOCK(inp); inp->inp_rss_listen_bucket = optval; - OPTSET2(INP_RSS_BUCKET_SET, 1); + OPTSET2_N(INP_RSS_BUCKET_SET, 1); + INP_WUNLOCK(inp); } else { error = EINVAL; } @@ -1242,6 +1253,7 @@ switch (sopt->sopt_name) { case IP_OPTIONS: case IP_RETOPTS: + INP_RLOCK(inp); if (inp->inp_options) error = sooptcopyout(sopt, mtod(inp->inp_options, @@ -1249,6 +1261,7 @@ inp->inp_options->m_len); else sopt->sopt_valsize = 0; + INP_RUNLOCK(inp); break; case IP_TOS: @@ -1315,12 +1328,14 @@ break; case IP_PORTRANGE: + INP_RLOCK(inp); if (inp->inp_flags & INP_HIGHPORT) optval = IP_PORTRANGE_HIGH; else if (inp->inp_flags & INP_LOWPORT) optval = IP_PORTRANGE_LOW; else optval = 0; + INP_RUNLOCK(inp); break; case IP_ONESBCAST: @@ -1346,6 +1361,7 @@ break; #ifdef RSS case IP_RSSBUCKETID: + INP_RLOCK(inp); retval = rss_hash2bucket(inp->inp_flowid, inp->inp_flowtype, &rss_bucket); @@ -1353,6 +1369,7 @@ optval = rss_bucket; else error = EINVAL; + INP_RUNLOCK(inp); break; case IP_RECVRSSBUCKETID: optval = OPTBIT2(INP_RECVRSSBUCKETID); Index: sys/netinet/raw_ip.c =================================================================== --- sys/netinet/raw_ip.c +++ sys/netinet/raw_ip.c @@ -569,7 +569,9 @@ if (sopt->sopt_level != IPPROTO_IP) { if ((sopt->sopt_level == SOL_SOCKET) && (sopt->sopt_name == SO_SETFIB)) { + INP_WLOCK(inp); inp->inp_inc.inc_fibnum = so->so_fibnum; + INP_WUNLOCK(inp); return (0); } return (EINVAL); @@ -637,10 +639,12 @@ sizeof optval); if (error) break; + INP_WLOCK(inp); if (optval) inp->inp_flags |= INP_HDRINCL; else inp->inp_flags &= ~INP_HDRINCL; + INP_WUNLOCK(inp); break; case IP_FW3: /* generic ipfw v.3 functions */ 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); @@ -1496,8 +1496,10 @@ error = soopt_mcopyin(sopt, m); /* XXX */ if (error != 0) break; + INP_WLOCK(in6p); error = ip6_pcbopts(&in6p->in6p_outputopts, m, so, sopt); + INP_WUNLOCK(in6p); m_freem(m); /* XXX */ break; } @@ -1567,19 +1569,25 @@ error = EINVAL; else { /* -1 = kernel default */ + INP_WLOCK(in6p); in6p->in6p_hops = optval; if ((in6p->inp_vflag & INP_IPV4) != 0) in6p->inp_ip_ttl = optval; + INP_WUNLOCK(in6p); } break; -#define OPTSET(bit) \ +#define OPTSET_N(bit) \ do { \ - INP_WLOCK(in6p); \ if (optval) \ in6p->inp_flags |= (bit); \ else \ in6p->inp_flags &= ~(bit); \ +} while (/*CONSTCOND*/ 0) +#define OPTSET(bit) \ +do { \ + INP_WLOCK(in6p); \ + OPTSET_N(bit); \ INP_WUNLOCK(in6p); \ } while (/*CONSTCOND*/ 0) #define OPTSET2292(bit) \ @@ -1594,23 +1602,34 @@ } while (/*CONSTCOND*/ 0) #define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0) -#define OPTSET2(bit, val) do { \ - INP_WLOCK(in6p); \ +#define OPTSET2_N(bit, val) do { \ if (val) \ in6p->inp_flags2 |= bit; \ else \ in6p->inp_flags2 &= ~bit; \ +} while (0) +#define OPTSET2(bit, val) do { \ + INP_WLOCK(in6p); \ + OPTSET2_N(bit, val); \ INP_WUNLOCK(in6p); \ } while (0) #define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0) +#define OPTSET2292_EXCLUSIVE(bit) \ +do { \ + INP_WLOCK(in6p); \ + if (OPTBIT(IN6P_RFC2292)) { \ + error = EINVAL; \ + } else { \ + if (optval) \ + in6p->inp_flags |= (bit); \ + else \ + in6p->inp_flags &= ~(bit); \ + } \ + INP_WUNLOCK(in6p); \ +} 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,57 +1641,34 @@ error = EINVAL; break; } + INP_WLOCK(in6p); optp = &in6p->in6p_outputopts; error = ip6_pcbopt(IPV6_HOPLIMIT, (u_char *)&optval, sizeof(optval), optp, (td != NULL) ? td->td_ucred : NULL, uproto); + INP_WUNLOCK(in6p); break; } 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,24 +1698,24 @@ * available only prior to bind(2). * see ipng mailing list, Jun 22 2001. */ + INP_WLOCK(in6p); if (in6p->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { error = EINVAL; + INP_WUNLOCK(in6p); break; } - OPTSET(IN6P_IPV6_V6ONLY); + OPTSET_N(IN6P_IPV6_V6ONLY); + /* optclear vflag */ if (optval) in6p->inp_vflag &= ~INP_IPV4; else in6p->inp_vflag |= INP_IPV4; + INP_WUNLOCK(in6p); 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 +1735,10 @@ case IPV6_RSS_LISTEN_BUCKET: if ((optval >= 0) && (optval < rss_getnumbuckets())) { + INP_WLOCK(in6p); in6p->inp_rss_listen_bucket = optval; - OPTSET2(INP_RSS_BUCKET_SET, 1); + OPTSET2_N(INP_RSS_BUCKET_SET, 1); + INP_WUNLOCK(in6p); } else { error = EINVAL; } @@ -1763,11 +1761,13 @@ break; { struct ip6_pktopts **optp; + INP_WLOCK(in6p); optp = &in6p->in6p_outputopts; error = ip6_pcbopt(optname, (u_char *)&optval, sizeof(optval), optp, (td != NULL) ? td->td_ucred : NULL, uproto); + INP_WUNLOCK(in6p); break; } @@ -1832,12 +1832,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,12 +1841,21 @@ sizeof(optbuf_storage), 0); if (error) break; + + INP_WLOCK(in6p); + /* cannot mix with RFC2292 */ + if (OPTBIT(IN6P_RFC2292)) { + error = EINVAL; + INP_WUNLOCK(in6p); + break; + } optlen = sopt->sopt_valsize; optbuf = optbuf_storage; optp = &in6p->in6p_outputopts; error = ip6_pcbopt(optname, optbuf, optlen, optp, (td != NULL) ? td->td_ucred : NULL, uproto); + INP_WUNLOCK(in6p); break; } #undef OPTSET @@ -1999,6 +2002,7 @@ case IPV6_PORTRANGE: { int flags; + INP_RLOCK(in6p); flags = in6p->inp_flags; if (flags & INP_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; @@ -2006,6 +2010,7 @@ optval = IPV6_PORTRANGE_LOW; else optval = 0; + INP_RUNLOCK(in6p); break; } case IPV6_RECVTCLASS: @@ -2037,6 +2042,7 @@ break; #ifdef RSS case IPV6_RSSBUCKETID: + INP_RLOCK(in6p); retval = rss_hash2bucket(in6p->inp_flowid, in6p->inp_flowtype, @@ -2045,6 +2051,7 @@ optval = rss_bucket; else error = EINVAL; + INP_RUNLOCK(in6p); break; case IPV6_RECVRSSBUCKETID: @@ -2067,6 +2074,7 @@ { u_long pmtu = 0; struct ip6_mtuinfo mtuinfo; + struct in6_addr addr; if (!(so->so_state & SS_ISCONNECTED)) return (ENOTCONN); @@ -2074,9 +2082,15 @@ * 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); + bzero(&addr, sizeof(addr)); + 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 +2140,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: @@ -2199,15 +2212,19 @@ IPPROTO_ICMPV6) { if (optval != icmp6off) error = EINVAL; - } else + } else { + INP_WLOCK(in6p); in6p->in6p_cksum = optval; + INP_WUNLOCK(in6p); + } break; case SOPT_GET: if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) optval = icmp6off; - else + else { optval = in6p->in6p_cksum; + } error = sooptcopyout(sopt, &optval, sizeof(optval)); break; @@ -2238,6 +2255,8 @@ int error = 0; struct thread *td = sopt->sopt_td; + INP_WLOCK_ASSERT(sotoinpcb(so)); + /* turn off any old options. */ if (opt) { #ifdef DIAGNOSTIC @@ -2303,7 +2322,7 @@ } 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; int optdatalen = 0; @@ -2313,6 +2332,10 @@ 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: @@ -2394,8 +2417,10 @@ #ifdef DIAGNOSTIC panic("ip6_getpcbopt: unexpected option\n"); #endif + INP_RUNLOCK(in6p); return (ENOPROTOOPT); } + INP_RUNLOCK(in6p); error = sooptcopyout(sopt, optdata, optdatalen); @@ -3082,6 +3107,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) { @@ -283,6 +285,7 @@ case IPSEC_DIR_OUTBOUND: break; default: + INP_WUNLOCK(inp); ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, xpl->sadb_x_policy_dir)); return (EINVAL); @@ -300,8 +303,10 @@ return (EACCES); /* Allocate new SP entry. */ newsp = key_msg2sp(xpl, len, &error); - if (newsp == NULL) + if (newsp == NULL) { + INP_WUNLOCK(inp); return (error); + } newsp->state = IPSEC_SPSTATE_PCB; newsp->spidx.ul_proto = IPSEC_ULPROTO_ANY; #ifdef INET @@ -329,10 +334,10 @@ break; default: /* Other security policy types aren't allowed for PCB */ + INP_WUNLOCK(inp); return (EINVAL); } - INP_WLOCK(inp); if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) { spp = &inp->inp_sp->sp_in; flags = INP_INBOUND_POLICY; @@ -365,7 +370,7 @@ xpl = (struct sadb_x_policy *)request; - INP_RLOCK(inp); + INP_RLOCK_ASSERT(inp); flags = inp->inp_sp->flags; /* Select direction. */ switch (xpl->sadb_x_policy_dir) { @@ -421,9 +426,6 @@ size_t optlen; int error; - if (inp->inp_sp == NULL) - return (ENOPROTOOPT); - /* Limit maximum request size to PAGE_SIZE */ optlen = sopt->sopt_valsize; if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) @@ -439,12 +441,34 @@ */ error = sooptcopyin(sopt, optdata, optlen, optlen); if (error == 0) { - if (sopt->sopt_dir == SOPT_SET) + if (sopt->sopt_dir == SOPT_SET) { + INP_WLOCK(inp); + if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { + INP_WUNLOCK(inp); + return (ECONNRESET); + } + if (inp->inp_sp == NULL) { + INP_WUNLOCK(inp); + return (ENOPROTOOPT); + } + error = ipsec_set_pcbpolicy(inp, sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL, optdata, optlen); - else { + /* ipsec_set_pcbpolicy unlocks */ + INP_WUNLOCK(inp); + } else { + INP_RLOCK(inp); + if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { + INP_RUNLOCK(inp); + return (ECONNRESET); + } + if (inp->inp_sp == NULL) { + INP_RUNLOCK(inp); + return (ENOPROTOOPT); + } error = ipsec_get_pcbpolicy(inp, optdata, &optlen); + /* ipsec_get_pcbpolicy unlocks */ if (error == 0) error = sooptcopyout(sopt, optdata, optlen); } Index: sys/netipsec/xform_tcp.c =================================================================== --- sys/netipsec/xform_tcp.c +++ sys/netipsec/xform_tcp.c @@ -78,21 +78,31 @@ struct tcpcb *tp; int error, optval; - INP_WLOCK_ASSERT(inp); if (sopt->sopt_name != TCP_MD5SIG) { - INP_WUNLOCK(inp); return (ENOPROTOOPT); } - tp = intotcpcb(inp); if (sopt->sopt_dir == SOPT_GET) { + INP_RLOCK(inp); + if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { + INP_RUNLOCK(inp); + return (ECONNRESET); + } + tp = intotcpcb(inp); optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; - INP_WUNLOCK(inp); + INP_RUNLOCK(inp); - /* On success return with released INP_WLOCK */ + /* On success return with released INP_RLOCK */ return (sooptcopyout(sopt, &optval, sizeof(optval))); } + INP_WLOCK(inp); + if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { + INP_WUNLOCK(inp); + return (ECONNRESET); + } + tp = intotcpcb(inp); + INP_WUNLOCK(inp); error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); @@ -109,8 +119,9 @@ tp->t_flags |= TF_SIGNATURE; else tp->t_flags &= ~TF_SIGNATURE; + INP_WUNLOCK(inp); - /* On success return with acquired INP_WLOCK */ + /* On success return with released INP_WLOCK */ return (error); }