Hello - This diff splits the ctloutput functions into getopt/setopt, which could offer more fine-grained locking. It also removes some indentation and imo is easier to read.
Thoughts? Index: net/rtsock.c =================================================================== RCS file: /cvs/src/sys/net/rtsock.c,v retrieving revision 1.279 diff -u -p -r1.279 rtsock.c --- net/rtsock.c 10 Jul 2018 20:28:34 -0000 1.279 +++ net/rtsock.c 18 Oct 2018 15:26:28 -0000 @@ -110,6 +110,8 @@ void rcb_unref(void *, void *); int route_output(struct mbuf *, struct socket *, struct sockaddr *, struct mbuf *); int route_ctloutput(int, struct socket *, int, int, struct mbuf *); +int route_getopt(struct socket *, int, int, struct mbuf *); +int route_setopt(struct socket *, int, int, struct mbuf *); int route_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *); void route_input(struct mbuf *m0, struct socket *, sa_family_t); @@ -358,69 +360,98 @@ int route_ctloutput(int op, struct socket *so, int level, int optname, struct mbuf *m) { + int error; + + switch (op) { + case PRCO_SETOPT: + error = route_setopt(so, level, optname, m); + break; + case PRCO_GETOPT: + error = route_getopt(so, level, optname, m); + break; + default: + error = EINVAL; + break; + } + + return error; +} + +int +route_setopt(struct socket *so, int level, int optname, struct mbuf *m) +{ struct rtpcb *rop = sotortpcb(so); int error = 0; unsigned int tid, prio; if (level != AF_ROUTE) - return (EINVAL); + return EINVAL; - switch (op) { - case PRCO_SETOPT: - switch (optname) { - case ROUTE_MSGFILTER: - if (m == NULL || m->m_len != sizeof(unsigned int)) - error = EINVAL; - else - rop->rop_msgfilter = *mtod(m, unsigned int *); - break; - case ROUTE_TABLEFILTER: - if (m == NULL || m->m_len != sizeof(unsigned int)) { - error = EINVAL; - break; - } - tid = *mtod(m, unsigned int *); - if (tid != RTABLE_ANY && !rtable_exists(tid)) - error = ENOENT; - else - rop->rop_rtableid = tid; - break; - case ROUTE_PRIOFILTER: - if (m == NULL || m->m_len != sizeof(unsigned int)) { - error = EINVAL; - break; - } - prio = *mtod(m, unsigned int *); - if (prio > RTP_MAX) - error = EINVAL; - else - rop->rop_priority = prio; - break; - default: - error = ENOPROTOOPT; + switch (optname) { + case ROUTE_MSGFILTER: + if (m == NULL || m->m_len != sizeof(unsigned int)) + error = EINVAL; + else + rop->rop_msgfilter = *mtod(m, unsigned int *); + break; + case ROUTE_TABLEFILTER: + if (m == NULL || m->m_len != sizeof(unsigned int)) { + error = EINVAL; break; } + tid = *mtod(m, unsigned int *); + if (tid != RTABLE_ANY && !rtable_exists(tid)) + error = ENOENT; + else + rop->rop_rtableid = tid; break; - case PRCO_GETOPT: - switch (optname) { - case ROUTE_MSGFILTER: - m->m_len = sizeof(unsigned int); - *mtod(m, unsigned int *) = rop->rop_msgfilter; - break; - case ROUTE_TABLEFILTER: - m->m_len = sizeof(unsigned int); - *mtod(m, unsigned int *) = rop->rop_rtableid; - break; - case ROUTE_PRIOFILTER: - m->m_len = sizeof(unsigned int); - *mtod(m, unsigned int *) = rop->rop_priority; - break; - default: - error = ENOPROTOOPT; + case ROUTE_PRIOFILTER: + if (m == NULL || m->m_len != sizeof(unsigned int)) { + error = EINVAL; break; } + prio = *mtod(m, unsigned int *); + if (prio > RTP_MAX) + error = EINVAL; + else + rop->rop_priority = prio; + break; + default: + error = ENOPROTOOPT; + break; } - return (error); + + return error; +} + +int +route_getopt(struct socket *so, int level, int optname, struct mbuf *m) +{ + struct rtpcb *rop = sotortpcb(so); + int error = 0; + + if (level != AF_ROUTE) + return EINVAL; + + switch (optname) { + case ROUTE_MSGFILTER: + m->m_len = sizeof(unsigned int); + *mtod(m, unsigned int *) = rop->rop_msgfilter; + break; + case ROUTE_TABLEFILTER: + m->m_len = sizeof(unsigned int); + *mtod(m, unsigned int *) = rop->rop_rtableid; + break; + case ROUTE_PRIOFILTER: + m->m_len = sizeof(unsigned int); + *mtod(m, unsigned int *) = rop->rop_priority; + break; + default: + error = ENOPROTOOPT; + break; + } + + return error; } void Index: netinet6/icmp6.c =================================================================== RCS file: /cvs/src/sys/netinet6/icmp6.c,v retrieving revision 1.226 diff -u -p -r1.226 icmp6.c --- netinet6/icmp6.c 5 Sep 2018 09:47:18 -0000 1.226 +++ netinet6/icmp6.c 18 Oct 2018 15:26:28 -0000 @@ -1675,66 +1675,79 @@ int icmp6_ctloutput(int op, struct socket *so, int level, int optname, struct mbuf *m) { - int error = 0; + int error; + + switch (op) { + case PRCO_SETOPT: + error = icmp6_setopt(so, level, optname, m); + break; + case PRCO_GETOPT: + error = icmp6_getopt(so, level, optname, m); + break; + default: + error = EINVAL; + break; + } + + return error; +} + +int +icmp6_setopt(struct socket *so, int level, int optname, struct mbuf *m) +{ struct inpcb *in6p = sotoinpcb(so); + struct icmp6_filter *p; + int error = 0; if (level != IPPROTO_ICMPV6) return EINVAL; - switch (op) { - case PRCO_SETOPT: - switch (optname) { - case ICMP6_FILTER: - { - struct icmp6_filter *p; - - if (m == NULL || m->m_len != sizeof(*p)) { - error = EMSGSIZE; - break; - } - p = mtod(m, struct icmp6_filter *); - if (!p || !in6p->inp_icmp6filt) { - error = EINVAL; - break; - } - bcopy(p, in6p->inp_icmp6filt, - sizeof(struct icmp6_filter)); - error = 0; + switch (optname) { + case ICMP6_FILTER: + if (m == NULL || m->m_len != sizeof(*p)) { + error = EMSGSIZE; break; - } - - default: - error = ENOPROTOOPT; + } + p = mtod(m, struct icmp6_filter *); + if (!p || !in6p->inp_icmp6filt) { + error = EINVAL; break; } + bcopy(p, in6p->inp_icmp6filt, + sizeof(struct icmp6_filter)); + break; + default: + error = ENOPROTOOPT; break; + } - case PRCO_GETOPT: - switch (optname) { - case ICMP6_FILTER: - { - struct icmp6_filter *p; - - if (!in6p->inp_icmp6filt) { - error = EINVAL; - break; - } - m->m_len = sizeof(struct icmp6_filter); - p = mtod(m, struct icmp6_filter *); - bcopy(in6p->inp_icmp6filt, p, - sizeof(struct icmp6_filter)); - error = 0; - break; - } + return error; +} + +int +icmp6_getopt(struct socket *so, int level, int optname, struct mbuf *m) +{ + struct inpcb *in6p = sotoinpcb(so); + struct icmp6_filter *p; + int error = 0; - default: - error = ENOPROTOOPT; + switch (optname) { + case ICMP6_FILTER: + if (!in6p->inp_icmp6filt) { + error = EINVAL; break; } + m->m_len = sizeof(struct icmp6_filter); + p = mtod(m, struct icmp6_filter *); + bcopy(in6p->inp_icmp6filt, p, + sizeof(struct icmp6_filter)); + break; + default: + error = ENOPROTOOPT; break; } - return (error); + return error; } /* Index: netinet6/ip6_output.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_output.c,v retrieving revision 1.239 diff -u -p -r1.239 ip6_output.c --- netinet6/ip6_output.c 28 Aug 2018 15:15:02 -0000 1.239 +++ netinet6/ip6_output.c 18 Oct 2018 15:26:28 -0000 @@ -121,6 +121,8 @@ int ip6_getpcbopt(struct ip6_pktopts *, int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, int, int, int); int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *, unsigned int); int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf *); +int ip6_raw_getopt(struct socket *, int, int, struct mbuf *); +int ip6_raw_setopt(struct socket *, int, int, struct mbuf *); int ip6_copyexthdr(struct mbuf **, caddr_t, int); int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, struct ip6_frag **); @@ -1047,8 +1049,27 @@ int ip6_ctloutput(int op, struct socket *so, int level, int optname, struct mbuf *m) { - int privileged, optdatalen, uproto; - void *optdata; + int error; + + switch (op) { + case PRCO_SETOPT: + error = ip6_setopt(so, level, optname, m); + break; + case PRCO_GETOPT: + error = ip6_getopt(so, level, optname, m); + break; + default: + error = EINVAL; + break; + } + + return error; +} + +int +ip6_setopt(struct socket *so, int level, int optname, struct mbuf *m) +{ + int privileged, uproto; struct inpcb *inp = sotoinpcb(so); int error, optval; struct proc *p = curproc; /* For IPsec and rdomain */ @@ -1062,63 +1083,61 @@ ip6_ctloutput(int op, struct socket *so, if (level != IPPROTO_IPV6) return (EINVAL); - switch (op) { - case PRCO_SETOPT: + switch (optname) { + /* + * Use of some Hop-by-Hop options or some + * Destination options, might require special + * privilege. That is, normal applications + * (without special privilege) might be forbidden + * from setting certain options in outgoing packets, + * and might never see certain options in received + * packets. [RFC 2292 Section 6] + * KAME specific note: + * KAME prevents non-privileged users from sending or + * receiving ANY hbh/dst options in order to avoid + * overhead of parsing options in the kernel. + */ + case IPV6_RECVHOPOPTS: + case IPV6_RECVDSTOPTS: + if (!privileged) { + error = EPERM; + break; + } + /* FALLTHROUGH */ + case IPV6_UNICAST_HOPS: + case IPV6_MINHOPCOUNT: + case IPV6_HOPLIMIT: + + case IPV6_RECVPKTINFO: + case IPV6_RECVHOPLIMIT: + case IPV6_RECVRTHDR: + case IPV6_RECVPATHMTU: + case IPV6_RECVTCLASS: + case IPV6_V6ONLY: + case IPV6_AUTOFLOWLABEL: + case IPV6_RECVDSTPORT: + if (m == NULL || m->m_len != sizeof(int)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); switch (optname) { - /* - * Use of some Hop-by-Hop options or some - * Destination options, might require special - * privilege. That is, normal applications - * (without special privilege) might be forbidden - * from setting certain options in outgoing packets, - * and might never see certain options in received - * packets. [RFC 2292 Section 6] - * KAME specific note: - * KAME prevents non-privileged users from sending or - * receiving ANY hbh/dst options in order to avoid - * overhead of parsing options in the kernel. - */ - case IPV6_RECVHOPOPTS: - case IPV6_RECVDSTOPTS: - if (!privileged) { - error = EPERM; - break; - } - /* FALLTHROUGH */ - case IPV6_UNICAST_HOPS: - case IPV6_MINHOPCOUNT: - case IPV6_HOPLIMIT: - case IPV6_RECVPKTINFO: - case IPV6_RECVHOPLIMIT: - case IPV6_RECVRTHDR: - case IPV6_RECVPATHMTU: - case IPV6_RECVTCLASS: - case IPV6_V6ONLY: - case IPV6_AUTOFLOWLABEL: - case IPV6_RECVDSTPORT: - if (m == NULL || m->m_len != sizeof(int)) { + case IPV6_UNICAST_HOPS: + if (optval < -1 || optval >= 256) error = EINVAL; - break; + else { + /* -1 = kernel default */ + inp->inp_hops = optval; } - optval = *mtod(m, int *); - switch (optname) { - - case IPV6_UNICAST_HOPS: - if (optval < -1 || optval >= 256) - error = EINVAL; - else { - /* -1 = kernel default */ - inp->inp_hops = optval; - } - break; + break; - case IPV6_MINHOPCOUNT: - if (optval < 0 || optval > 255) - error = EINVAL; - else - inp->inp_ip6_minhlim = optval; - break; + case IPV6_MINHOPCOUNT: + if (optval < 0 || optval > 255) + error = EINVAL; + else + inp->inp_ip6_minhlim = optval; + break; #define OPTSET(bit) \ do { \ @@ -1129,479 +1148,512 @@ do { \ } while (/*CONSTCOND*/ 0) #define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0) - case IPV6_RECVPKTINFO: - OPTSET(IN6P_PKTINFO); - break; - - case IPV6_HOPLIMIT: - { - struct ip6_pktopts **optp; - - optp = &inp->inp_outputopts6; - error = ip6_pcbopt(IPV6_HOPLIMIT, - (u_char *)&optval, - sizeof(optval), - optp, - privileged, uproto); - break; - } + case IPV6_RECVPKTINFO: + OPTSET(IN6P_PKTINFO); + break; - case IPV6_RECVHOPLIMIT: - OPTSET(IN6P_HOPLIMIT); - break; + case IPV6_HOPLIMIT: + { + struct ip6_pktopts **optp; - case IPV6_RECVHOPOPTS: - OPTSET(IN6P_HOPOPTS); - break; + optp = &inp->inp_outputopts6; + error = ip6_pcbopt(IPV6_HOPLIMIT, + (u_char *)&optval, + sizeof(optval), + optp, + privileged, uproto); + break; + } - case IPV6_RECVDSTOPTS: - OPTSET(IN6P_DSTOPTS); - break; + case IPV6_RECVHOPLIMIT: + OPTSET(IN6P_HOPLIMIT); + break; - case IPV6_RECVRTHDR: - OPTSET(IN6P_RTHDR); - break; + case IPV6_RECVHOPOPTS: + OPTSET(IN6P_HOPOPTS); + break; - case IPV6_RECVPATHMTU: - /* - * We ignore this option for TCP - * sockets. - * (RFC3542 leaves this case - * unspecified.) - */ - if (uproto != IPPROTO_TCP) - OPTSET(IN6P_MTU); - break; + case IPV6_RECVDSTOPTS: + OPTSET(IN6P_DSTOPTS); + break; - case IPV6_V6ONLY: - /* - * make setsockopt(IPV6_V6ONLY) - * available only prior to bind(2). - * see ipng mailing list, Jun 22 2001. - */ - if (inp->inp_lport || - !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) { - error = EINVAL; - break; - } - /* No support for IPv4-mapped addresses. */ - if (!optval) - error = EINVAL; - else - error = 0; - break; - case IPV6_RECVTCLASS: - OPTSET(IN6P_TCLASS); - break; - case IPV6_AUTOFLOWLABEL: - OPTSET(IN6P_AUTOFLOWLABEL); - break; + case IPV6_RECVRTHDR: + OPTSET(IN6P_RTHDR); + break; - case IPV6_RECVDSTPORT: - OPTSET(IN6P_RECVDSTPORT); - break; - } + case IPV6_RECVPATHMTU: + /* + * We ignore this option for TCP + * sockets. + * (RFC3542 leaves this case + * unspecified.) + */ + if (uproto != IPPROTO_TCP) + OPTSET(IN6P_MTU); break; - case IPV6_TCLASS: - case IPV6_DONTFRAG: - case IPV6_USE_MIN_MTU: - if (m == NULL || m->m_len != sizeof(optval)) { + case IPV6_V6ONLY: + /* + * make setsockopt(IPV6_V6ONLY) + * available only prior to bind(2). + * see ipng mailing list, Jun 22 2001. + */ + if (inp->inp_lport || + !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) { error = EINVAL; break; } - optval = *mtod(m, int *); - { - struct ip6_pktopts **optp; - optp = &inp->inp_outputopts6; - error = ip6_pcbopt(optname, - (u_char *)&optval, - sizeof(optval), - optp, - privileged, uproto); - break; - } + /* No support for IPv4-mapped addresses. */ + if (!optval) + error = EINVAL; + else + error = 0; + break; + case IPV6_RECVTCLASS: + OPTSET(IN6P_TCLASS); + break; + case IPV6_AUTOFLOWLABEL: + OPTSET(IN6P_AUTOFLOWLABEL); + break; + + case IPV6_RECVDSTPORT: + OPTSET(IN6P_RECVDSTPORT); + break; + } + break; - case IPV6_PKTINFO: - case IPV6_HOPOPTS: - case IPV6_RTHDR: - case IPV6_DSTOPTS: - case IPV6_RTHDRDSTOPTS: + case IPV6_TCLASS: + case IPV6_DONTFRAG: + case IPV6_USE_MIN_MTU: + if (m == NULL || m->m_len != sizeof(optval)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); { - /* new advanced API (RFC3542) */ - u_char *optbuf; - int optbuflen; struct ip6_pktopts **optp; - - if (m && m->m_next) { - error = EINVAL; /* XXX */ - break; - } - if (m) { - optbuf = mtod(m, u_char *); - optbuflen = m->m_len; - } else { - optbuf = NULL; - optbuflen = 0; - } optp = &inp->inp_outputopts6; error = ip6_pcbopt(optname, - optbuf, optbuflen, - optp, privileged, uproto); + (u_char *)&optval, + sizeof(optval), + optp, + privileged, uproto); break; } -#undef OPTSET - case IPV6_MULTICAST_IF: - case IPV6_MULTICAST_HOPS: - case IPV6_MULTICAST_LOOP: - case IPV6_JOIN_GROUP: - case IPV6_LEAVE_GROUP: - error = ip6_setmoptions(optname, - &inp->inp_moptions6, - m, inp->inp_rtableid); + case IPV6_PKTINFO: + case IPV6_HOPOPTS: + case IPV6_RTHDR: + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + { + /* new advanced API (RFC3542) */ + u_char *optbuf; + int optbuflen; + struct ip6_pktopts **optp; + + if (m && m->m_next) { + error = EINVAL; /* XXX */ break; + } + if (m) { + optbuf = mtod(m, u_char *); + optbuflen = m->m_len; + } else { + optbuf = NULL; + optbuflen = 0; + } + optp = &inp->inp_outputopts6; + error = ip6_pcbopt(optname, + optbuf, optbuflen, + optp, privileged, uproto); + break; + } +#undef OPTSET - case IPV6_PORTRANGE: - if (m == NULL || m->m_len != sizeof(int)) { - error = EINVAL; - break; - } - optval = *mtod(m, int *); + case IPV6_MULTICAST_IF: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + error = ip6_setmoptions(optname, + &inp->inp_moptions6, + m, inp->inp_rtableid); + break; - switch (optval) { - case IPV6_PORTRANGE_DEFAULT: - inp->inp_flags &= ~(IN6P_LOWPORT); - inp->inp_flags &= ~(IN6P_HIGHPORT); - break; + case IPV6_PORTRANGE: + if (m == NULL || m->m_len != sizeof(int)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); - case IPV6_PORTRANGE_HIGH: - inp->inp_flags &= ~(IN6P_LOWPORT); - inp->inp_flags |= IN6P_HIGHPORT; - break; + switch (optval) { + case IPV6_PORTRANGE_DEFAULT: + inp->inp_flags &= ~(IN6P_LOWPORT); + inp->inp_flags &= ~(IN6P_HIGHPORT); + break; - case IPV6_PORTRANGE_LOW: - inp->inp_flags &= ~(IN6P_HIGHPORT); - inp->inp_flags |= IN6P_LOWPORT; - break; + case IPV6_PORTRANGE_HIGH: + inp->inp_flags &= ~(IN6P_LOWPORT); + inp->inp_flags |= IN6P_HIGHPORT; + break; - default: - error = EINVAL; - break; - } + case IPV6_PORTRANGE_LOW: + inp->inp_flags &= ~(IN6P_HIGHPORT); + inp->inp_flags |= IN6P_LOWPORT; break; - case IPSEC6_OUTSA: + default: error = EINVAL; break; + } + break; - case IPV6_AUTH_LEVEL: - case IPV6_ESP_TRANS_LEVEL: - case IPV6_ESP_NETWORK_LEVEL: - case IPV6_IPCOMP_LEVEL: + case IPSEC6_OUTSA: + error = EINVAL; + break; + + case IPV6_AUTH_LEVEL: + case IPV6_ESP_TRANS_LEVEL: + case IPV6_ESP_NETWORK_LEVEL: + case IPV6_IPCOMP_LEVEL: #ifndef IPSEC - error = EINVAL; + error = EINVAL; #else - if (m == NULL || m->m_len != sizeof(int)) { - error = EINVAL; - break; - } - optval = *mtod(m, int *); + if (m == NULL || m->m_len != sizeof(int)) { + error = EINVAL; + break; + } + optval = *mtod(m, int *); - if (optval < IPSEC_LEVEL_BYPASS || - optval > IPSEC_LEVEL_UNIQUE) { - error = EINVAL; - break; - } + if (optval < IPSEC_LEVEL_BYPASS || + optval > IPSEC_LEVEL_UNIQUE) { + error = EINVAL; + break; + } - switch (optname) { - case IPV6_AUTH_LEVEL: - if (optval < IPSEC_AUTH_LEVEL_DEFAULT && - suser(p)) { - error = EACCES; - break; - } - inp->inp_seclevel[SL_AUTH] = optval; + switch (optname) { + case IPV6_AUTH_LEVEL: + if (optval < IPSEC_AUTH_LEVEL_DEFAULT && + suser(p)) { + error = EACCES; break; + } + inp->inp_seclevel[SL_AUTH] = optval; + break; - case IPV6_ESP_TRANS_LEVEL: - if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT && - suser(p)) { - error = EACCES; - break; - } - inp->inp_seclevel[SL_ESP_TRANS] = optval; + case IPV6_ESP_TRANS_LEVEL: + if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT && + suser(p)) { + error = EACCES; break; + } + inp->inp_seclevel[SL_ESP_TRANS] = optval; + break; - case IPV6_ESP_NETWORK_LEVEL: - if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT && - suser(p)) { - error = EACCES; - break; - } - inp->inp_seclevel[SL_ESP_NETWORK] = optval; + case IPV6_ESP_NETWORK_LEVEL: + if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT && + suser(p)) { + error = EACCES; break; + } + inp->inp_seclevel[SL_ESP_NETWORK] = optval; + break; - case IPV6_IPCOMP_LEVEL: - if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT && - suser(p)) { - error = EACCES; - break; - } - inp->inp_seclevel[SL_IPCOMP] = optval; + case IPV6_IPCOMP_LEVEL: + if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT && + suser(p)) { + error = EACCES; break; } + inp->inp_seclevel[SL_IPCOMP] = optval; + break; + } #endif + break; + case SO_RTABLE: + if (m == NULL || m->m_len < sizeof(u_int)) { + error = EINVAL; break; - case SO_RTABLE: - if (m == NULL || m->m_len < sizeof(u_int)) { - error = EINVAL; - break; - } - rtid = *mtod(m, u_int *); - if (inp->inp_rtableid == rtid) - break; - /* needs privileges to switch when already set */ - if (p->p_p->ps_rtableid != rtid && - p->p_p->ps_rtableid != 0 && - (error = suser(p)) != 0) - break; - /* table must exist */ - if (!rtable_exists(rtid)) { - error = EINVAL; - break; - } - if (inp->inp_lport) { - error = EBUSY; - break; - } - inp->inp_rtableid = rtid; - in_pcbrehash(inp); + } + rtid = *mtod(m, u_int *); + if (inp->inp_rtableid == rtid) break; - case IPV6_PIPEX: - if (m != NULL && m->m_len == sizeof(int)) - inp->inp_pipex = *mtod(m, int *); - else - error = EINVAL; + /* needs privileges to switch when already set */ + if (p->p_p->ps_rtableid != rtid && + p->p_p->ps_rtableid != 0 && + (error = suser(p)) != 0) break; - - default: - error = ENOPROTOOPT; + /* table must exist */ + if (!rtable_exists(rtid)) { + error = EINVAL; + break; + } + if (inp->inp_lport) { + error = EBUSY; break; } + inp->inp_rtableid = rtid; + in_pcbrehash(inp); + break; + case IPV6_PIPEX: + if (m != NULL && m->m_len == sizeof(int)) + inp->inp_pipex = *mtod(m, int *); + else + error = EINVAL; break; - case PRCO_GETOPT: - switch (optname) { + default: + error = ENOPROTOOPT; + break; + } - case IPV6_RECVHOPOPTS: - case IPV6_RECVDSTOPTS: - case IPV6_UNICAST_HOPS: - case IPV6_MINHOPCOUNT: - case IPV6_RECVPKTINFO: - case IPV6_RECVHOPLIMIT: - case IPV6_RECVRTHDR: - case IPV6_RECVPATHMTU: + return error; +} - case IPV6_V6ONLY: - case IPV6_PORTRANGE: - case IPV6_RECVTCLASS: - case IPV6_AUTOFLOWLABEL: - case IPV6_RECVDSTPORT: - switch (optname) { +int +ip6_getopt(struct socket *so, int level, int optname, struct mbuf *m) +{ + int optdatalen; + void *optdata; + struct inpcb *inp = sotoinpcb(so); + int error, optval; - case IPV6_RECVHOPOPTS: - optval = OPTBIT(IN6P_HOPOPTS); - break; + error = optval = 0; - case IPV6_RECVDSTOPTS: - optval = OPTBIT(IN6P_DSTOPTS); - break; + if (level != IPPROTO_IPV6) + return (EINVAL); - case IPV6_UNICAST_HOPS: - optval = inp->inp_hops; - break; + switch (optname) { - case IPV6_MINHOPCOUNT: - optval = inp->inp_ip6_minhlim; - break; + case IPV6_RECVHOPOPTS: + case IPV6_RECVDSTOPTS: + case IPV6_UNICAST_HOPS: + case IPV6_MINHOPCOUNT: + case IPV6_RECVPKTINFO: + case IPV6_RECVHOPLIMIT: + case IPV6_RECVRTHDR: + case IPV6_RECVPATHMTU: + + case IPV6_V6ONLY: + case IPV6_PORTRANGE: + case IPV6_RECVTCLASS: + case IPV6_AUTOFLOWLABEL: + case IPV6_RECVDSTPORT: + switch (optname) { - case IPV6_RECVPKTINFO: - optval = OPTBIT(IN6P_PKTINFO); - break; + case IPV6_RECVHOPOPTS: + optval = OPTBIT(IN6P_HOPOPTS); + break; - case IPV6_RECVHOPLIMIT: - optval = OPTBIT(IN6P_HOPLIMIT); - break; + case IPV6_RECVDSTOPTS: + optval = OPTBIT(IN6P_DSTOPTS); + break; - case IPV6_RECVRTHDR: - optval = OPTBIT(IN6P_RTHDR); - break; + case IPV6_UNICAST_HOPS: + optval = inp->inp_hops; + break; - case IPV6_RECVPATHMTU: - optval = OPTBIT(IN6P_MTU); - break; + case IPV6_MINHOPCOUNT: + optval = inp->inp_ip6_minhlim; + break; - case IPV6_V6ONLY: - optval = 1; - break; + case IPV6_RECVPKTINFO: + optval = OPTBIT(IN6P_PKTINFO); + break; - case IPV6_PORTRANGE: - { - int flags; - flags = inp->inp_flags; - if (flags & IN6P_HIGHPORT) - optval = IPV6_PORTRANGE_HIGH; - else if (flags & IN6P_LOWPORT) - optval = IPV6_PORTRANGE_LOW; - else - optval = 0; - break; - } - case IPV6_RECVTCLASS: - optval = OPTBIT(IN6P_TCLASS); - break; + case IPV6_RECVHOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); + break; - case IPV6_AUTOFLOWLABEL: - optval = OPTBIT(IN6P_AUTOFLOWLABEL); - break; + case IPV6_RECVRTHDR: + optval = OPTBIT(IN6P_RTHDR); + break; - case IPV6_RECVDSTPORT: - optval = OPTBIT(IN6P_RECVDSTPORT); - break; - } - if (error) - break; - m->m_len = sizeof(int); - *mtod(m, int *) = optval; + case IPV6_RECVPATHMTU: + optval = OPTBIT(IN6P_MTU); break; - case IPV6_PATHMTU: - { - u_long pmtu = 0; - struct ip6_mtuinfo mtuinfo; - struct ifnet *ifp; - struct rtentry *rt; - - if (!(so->so_state & SS_ISCONNECTED)) - return (ENOTCONN); - - rt = in_pcbrtentry(inp); - if (!rtisvalid(rt)) - return (EHOSTUNREACH); + case IPV6_V6ONLY: + optval = 1; + break; - ifp = if_get(rt->rt_ifidx); - if (ifp == NULL) - return (EHOSTUNREACH); - /* - * XXX: we dot not consider the case of source - * routing, or optional information to specify - * the outgoing interface. - */ - error = ip6_getpmtu(rt, ifp, &pmtu); - if_put(ifp); - if (error) - break; - if (pmtu > IPV6_MAXPACKET) - pmtu = IPV6_MAXPACKET; + case IPV6_PORTRANGE: + { + int flags; + flags = inp->inp_flags; + if (flags & IN6P_HIGHPORT) + optval = IPV6_PORTRANGE_HIGH; + else if (flags & IN6P_LOWPORT) + optval = IPV6_PORTRANGE_LOW; + else + optval = 0; + break; + } + case IPV6_RECVTCLASS: + optval = OPTBIT(IN6P_TCLASS); + break; - bzero(&mtuinfo, sizeof(mtuinfo)); - mtuinfo.ip6m_mtu = (u_int32_t)pmtu; - optdata = (void *)&mtuinfo; - optdatalen = sizeof(mtuinfo); - if (optdatalen > MCLBYTES) - return (EMSGSIZE); /* XXX */ - if (optdatalen > MLEN) - MCLGET(m, M_WAIT); - m->m_len = optdatalen; - bcopy(optdata, mtod(m, void *), optdatalen); + case IPV6_AUTOFLOWLABEL: + optval = OPTBIT(IN6P_AUTOFLOWLABEL); break; - } - case IPV6_PKTINFO: - case IPV6_HOPOPTS: - case IPV6_RTHDR: - case IPV6_DSTOPTS: - case IPV6_RTHDRDSTOPTS: - case IPV6_TCLASS: - case IPV6_DONTFRAG: - case IPV6_USE_MIN_MTU: - error = ip6_getpcbopt(inp->inp_outputopts6, - optname, m); - break; - - case IPV6_MULTICAST_IF: - case IPV6_MULTICAST_HOPS: - case IPV6_MULTICAST_LOOP: - case IPV6_JOIN_GROUP: - case IPV6_LEAVE_GROUP: - error = ip6_getmoptions(optname, - inp->inp_moptions6, m); + case IPV6_RECVDSTPORT: + optval = OPTBIT(IN6P_RECVDSTPORT); + break; + } + if (error) break; + m->m_len = sizeof(int); + *mtod(m, int *) = optval; + break; - case IPSEC6_OUTSA: - error = EINVAL; + case IPV6_PATHMTU: + { + u_long pmtu = 0; + struct ip6_mtuinfo mtuinfo; + struct ifnet *ifp; + struct rtentry *rt; + + if (!(so->so_state & SS_ISCONNECTED)) + return (ENOTCONN); + + rt = in_pcbrtentry(inp); + if (!rtisvalid(rt)) + return (EHOSTUNREACH); + + ifp = if_get(rt->rt_ifidx); + if (ifp == NULL) + return (EHOSTUNREACH); + /* + * XXX: we dot not consider the case of source + * routing, or optional information to specify + * the outgoing interface. + */ + error = ip6_getpmtu(rt, ifp, &pmtu); + if_put(ifp); + if (error) break; + if (pmtu > IPV6_MAXPACKET) + pmtu = IPV6_MAXPACKET; - case IPV6_AUTH_LEVEL: - case IPV6_ESP_TRANS_LEVEL: - case IPV6_ESP_NETWORK_LEVEL: - case IPV6_IPCOMP_LEVEL: -#ifndef IPSEC - m->m_len = sizeof(int); - *mtod(m, int *) = IPSEC_LEVEL_NONE; -#else - m->m_len = sizeof(int); - switch (optname) { - case IPV6_AUTH_LEVEL: - optval = inp->inp_seclevel[SL_AUTH]; - break; + bzero(&mtuinfo, sizeof(mtuinfo)); + mtuinfo.ip6m_mtu = (u_int32_t)pmtu; + optdata = (void *)&mtuinfo; + optdatalen = sizeof(mtuinfo); + if (optdatalen > MCLBYTES) + return (EMSGSIZE); /* XXX */ + if (optdatalen > MLEN) + MCLGET(m, M_WAIT); + m->m_len = optdatalen; + bcopy(optdata, mtod(m, void *), optdatalen); + break; + } - case IPV6_ESP_TRANS_LEVEL: - optval = - inp->inp_seclevel[SL_ESP_TRANS]; - break; + case IPV6_PKTINFO: + case IPV6_HOPOPTS: + case IPV6_RTHDR: + case IPV6_DSTOPTS: + case IPV6_RTHDRDSTOPTS: + case IPV6_TCLASS: + case IPV6_DONTFRAG: + case IPV6_USE_MIN_MTU: + error = ip6_getpcbopt(inp->inp_outputopts6, + optname, m); + break; - case IPV6_ESP_NETWORK_LEVEL: - optval = - inp->inp_seclevel[SL_ESP_NETWORK]; - break; + case IPV6_MULTICAST_IF: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + error = ip6_getmoptions(optname, + inp->inp_moptions6, m); + break; - case IPV6_IPCOMP_LEVEL: - optval = inp->inp_seclevel[SL_IPCOMP]; - break; - } - *mtod(m, int *) = optval; -#endif + case IPSEC6_OUTSA: + error = EINVAL; + break; + + case IPV6_AUTH_LEVEL: + case IPV6_ESP_TRANS_LEVEL: + case IPV6_ESP_NETWORK_LEVEL: + case IPV6_IPCOMP_LEVEL: +#ifndef IPSEC + m->m_len = sizeof(int); + *mtod(m, int *) = IPSEC_LEVEL_NONE; +#else + m->m_len = sizeof(int); + switch (optname) { + case IPV6_AUTH_LEVEL: + optval = inp->inp_seclevel[SL_AUTH]; break; - case SO_RTABLE: - m->m_len = sizeof(u_int); - *mtod(m, u_int *) = optval; - break; - case IPV6_PIPEX: - m->m_len = sizeof(int); - *mtod(m, int *) = optval; + + case IPV6_ESP_TRANS_LEVEL: + optval = + inp->inp_seclevel[SL_ESP_TRANS]; break; - default: - error = ENOPROTOOPT; + case IPV6_ESP_NETWORK_LEVEL: + optval = + inp->inp_seclevel[SL_ESP_NETWORK]; + break; + + case IPV6_IPCOMP_LEVEL: + optval = inp->inp_seclevel[SL_IPCOMP]; break; } + *mtod(m, int *) = optval; +#endif + break; + case SO_RTABLE: + m->m_len = sizeof(u_int); + *mtod(m, u_int *) = optval; + break; + case IPV6_PIPEX: + m->m_len = sizeof(int); + *mtod(m, int *) = optval; + break; + + default: + error = ENOPROTOOPT; break; } - return (error); + + return error; } int ip6_raw_ctloutput(int op, struct socket *so, int level, int optname, struct mbuf *m) { - int error = 0, optval; - const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); + int error; + + switch (op) { + case PRCO_SETOPT: + error = ip6_raw_setopt(so, level, optname, m); + break; + case PRCO_GETOPT: + error = ip6_raw_getopt(so, level, optname, m); + break; + default: + error = EINVAL; + break; + } + + return error; +} + +int +ip6_raw_setopt(struct socket *so, int level, int optname, struct mbuf *m) +{ struct inpcb *inp = sotoinpcb(so); + const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); + int error = 0, optval; if (level != IPPROTO_IPV6) - return (EINVAL); + return EINVAL; switch (optname) { case IPV6_CHECKSUM: @@ -1613,45 +1665,54 @@ ip6_raw_ctloutput(int op, struct socket * for an ICMPv6 socket will fail." * The current behavior does not meet RFC3542. */ - switch (op) { - case PRCO_SETOPT: - if (m == NULL || m->m_len != sizeof(int)) { - error = EINVAL; - break; - } - optval = *mtod(m, int *); - if ((optval % 2) != 0) { - /* the API assumes even offset values */ - error = EINVAL; - } else if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { - if (optval != icmp6off) - error = EINVAL; - } else - inp->inp_cksum6 = optval; - break; - - case PRCO_GETOPT: - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) - optval = icmp6off; - else - optval = inp->inp_cksum6; - - m->m_len = sizeof(int); - *mtod(m, int *) = optval; - break; - - default: + if (m == NULL || m->m_len != sizeof(int)) { error = EINVAL; break; } + optval = *mtod(m, int *); + if ((optval % 2) != 0) { + /* the API assumes even offset values */ + error = EINVAL; + } else if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (optval != icmp6off) + error = EINVAL; + } else + inp->inp_cksum6 = optval; + break; + default: + error = ENOPROTOOPT; break; + } + + return error; +} + +int +ip6_raw_getopt(struct socket *so, int level, int optname, struct mbuf *m) +{ + struct inpcb *inp = sotoinpcb(so); + const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); + int error = 0, optval; + if (level != IPPROTO_IPV6) + return EINVAL; + + switch (optname) { + case IPV6_CHECKSUM: + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) + optval = icmp6off; + else + optval = inp->inp_cksum6; + + m->m_len = sizeof(int); + *mtod(m, int *) = optval; + break; default: error = ENOPROTOOPT; break; } - return (error); + return error; } /* Index: netinet6/ip6_var.h =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_var.h,v retrieving revision 1.84 diff -u -p -r1.84 ip6_var.h --- netinet6/ip6_var.h 10 Oct 2018 11:46:59 -0000 1.84 +++ netinet6/ip6_var.h 18 Oct 2018 15:26:28 -0000 @@ -299,6 +299,8 @@ struct in6pcb; struct inpcb; int icmp6_ctloutput(int, struct socket *, int, int, struct mbuf *); +int icmp6_getopt(struct socket *, int, int, struct mbuf *); +int icmp6_setopt(struct socket *, int, int, struct mbuf *); void ip6_init(void); void ip6intr(void); @@ -322,6 +324,8 @@ int ip6_output(struct mbuf *, struct ip6 struct ip6_moptions *, struct inpcb *); int ip6_fragment(struct mbuf *, int, u_char, u_long); int ip6_ctloutput(int, struct socket *, int, int, struct mbuf *); +int ip6_getopt(struct socket *, int, int, struct mbuf *); +int ip6_setopt(struct socket *, int, int, struct mbuf *); int ip6_raw_ctloutput(int, struct socket *, int, int, struct mbuf *); void ip6_initpktopts(struct ip6_pktopts *); int ip6_setpktopts(struct mbuf *, struct ip6_pktopts *,