summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/ip6_output.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-08-21 09:39:55 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-21 10:29:40 +0200
commit2df56dbd60bb5d925d2ce0ddbdefdbe6107ea783 (patch)
treebd7bad558534db4a1f400bc38a2c9aa7ea4f411e /freebsd/sys/netinet6/ip6_output.c
parentUpdate to FreeBSD head 2018-02-01 (diff)
downloadrtems-libbsd-2df56dbd60bb5d925d2ce0ddbdefdbe6107ea783.tar.bz2
Update to FreeBSD head 2018-04-01
Git mirror commit 8dfb1ccc26d1cea7e2529303003ff61f9f1784c4. Update #3472.
Diffstat (limited to 'freebsd/sys/netinet6/ip6_output.c')
-rw-r--r--freebsd/sys/netinet6/ip6_output.c174
1 files changed, 89 insertions, 85 deletions
diff --git a/freebsd/sys/netinet6/ip6_output.c b/freebsd/sys/netinet6/ip6_output.c
index 8dd71077..1841829a 100644
--- a/freebsd/sys/netinet6/ip6_output.c
+++ b/freebsd/sys/netinet6/ip6_output.c
@@ -137,7 +137,7 @@ static int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **,
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);
@@ -787,7 +787,7 @@ again:
odst = ip6->ip6_dst;
/* Run through list of hooks for output packets. */
- error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, inp);
+ error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, 0, inp);
if (error != 0 || m == NULL)
goto done;
/* adjust pointer */
@@ -1050,7 +1050,7 @@ sendorfree:
m = m0->m_nextpkt;
m0->m_nextpkt = 0;
m_freem(m0);
- for (m0 = m; m; m = m0) {
+ for (; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
if (error == 0) {
@@ -1598,23 +1598,34 @@ do { \
} 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:
@@ -1635,48 +1646,23 @@ do { \
}
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:
@@ -1719,11 +1705,7 @@ do { \
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);
@@ -1743,8 +1725,10 @@ do { \
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;
}
@@ -2071,6 +2055,7 @@ do { \
{
u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo;
+ struct in6_addr addr;
if (!(so->so_state & SS_ISCONNECTED))
return (ENOTCONN);
@@ -2078,9 +2063,14 @@ do { \
* 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)
@@ -2130,8 +2120,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:
@@ -2306,17 +2295,50 @@ ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt,
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:
@@ -2333,50 +2355,29 @@ ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
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:
@@ -2389,19 +2390,22 @@ ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
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);
}