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 *,
> 

Reply via email to