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); }