On 18/10/18(Thu) 11:34, David Hill wrote:
> 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?
Splitting the read and write path makes sense, it will allow us to use
read & write locks :)
> 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 *,
>