svn commit: r331380 - head/sys/netinet6
Sean Bruno
sbruno at FreeBSD.org
Thu Mar 22 23:34:49 UTC 2018
Author: sbruno
Date: Thu Mar 22 23:34:48 2018
New Revision: 331380
URL: https://svnweb.freebsd.org/changeset/base/331380
Log:
Refactor ip6_getpcbopt() for better locking and memory management
Created GET_PKTOPT_EXT_HDR() and GET_PKTOPT_SOCKADDR() macros to
handle safely fetching options from in6p_outputopts, including
properly dealing with in6p locking and preparing memory for
sooptcopyout().
Changed the function signature of ip6_getpcbopt() to allow the
function to acquire and release locks on in6p as needed.
Submitted by: Jason Eggleston <jason at eggnet.com>
Sponsored by: Limelight Networks
Differential Revision: https://reviews.freebsd.org/D14619
Modified:
head/sys/netinet6/ip6_output.c
Modified: head/sys/netinet6/ip6_output.c
==============================================================================
--- head/sys/netinet6/ip6_output.c Thu Mar 22 22:29:32 2018 (r331379)
+++ head/sys/netinet6/ip6_output.c Thu Mar 22 23:34:48 2018 (r331380)
@@ -135,7 +135,7 @@ static int ip6_pcbopt(int, u_char *, int, struct ip6_p
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);
@@ -2132,8 +2132,7 @@ do { \
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:
@@ -2310,18 +2309,51 @@ ip6_pcbopt(int optname, u_char *buf, int len, struct i
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:
optdata = (void *)&null_pktinfo;
@@ -2337,50 +2369,29 @@ ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname,
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:
@@ -2393,19 +2404,22 @@ ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname,
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);
}
More information about the svn-src-all
mailing list