+
+/*
+ * ICMPv6 socket datagram option processing.
+ */
+int
+icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ if (kauth_cred_issuser(so->so_cred))
+ return icmp6_ctloutput(so, sopt);
+
+ if (sopt->sopt_level == IPPROTO_ICMPV6) {
+ switch (sopt->sopt_name) {
+ case ICMP6_FILTER:
+ return icmp6_ctloutput(so, sopt);
+ default:
+ return EPERM;
+ }
+ }
+
+ if (sopt->sopt_level != IPPROTO_IPV6)
+ return EINVAL;
+
+ switch (sopt->sopt_name) {
+ case IPV6_UNICAST_HOPS:
+ case IPV6_CHECKSUM:
+ case IPV6_V6ONLY:
+ case IPV6_USE_MIN_MTU:
+ case IPV6_RECVRTHDR:
+ case IPV6_RECVPKTINFO:
+ case IPV6_RECVHOPLIMIT:
+ case IPV6_PATHMTU:
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+ case IPV6_PORTRANGE:
+ case IPV6_IPSEC_POLICY:
+ case IPV6_RECVTCLASS:
+ case IPV6_TCLASS:
+ case IPV6_2292PKTOPTIONS:
+ case IPV6_2292PKTINFO:
+ case IPV6_2292HOPLIMIT:
+ case IPV6_2292HOPOPTS:
+ case IPV6_2292DSTOPTS:
+ case IPV6_2292RTHDR:
+ case IPV6_BOUND_IF:
+ case IPV6_NO_IFT_CELLULAR:
+
+ return ip6_ctloutput(so, sopt);
+
+ default:
+ return EPERM;
+ }
+}
+
+__private_extern__ int
+icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *nam, struct mbuf *control, struct proc *p)
+{
+#pragma unused(flags, p)
+ int error = 0;
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 tmp;
+ struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam;
+ struct icmp6_hdr *icmp6;
+
+ if (kauth_cred_issuser(so->so_cred))
+ return rip6_output(m, so, (struct sockaddr_in6 *)(void *)nam,
+ control, 0);
+
+ /* always copy sockaddr to avoid overwrites */
+ if (so->so_state & SS_ISCONNECTED) {
+ if (nam) {
+ m_freem(m);
+ return EISCONN;
+ }
+ /* XXX */
+ bzero(&tmp, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
+ sizeof(struct in6_addr));
+ dst = &tmp;
+ } else {
+ if (nam == NULL) {
+ m_freem(m);
+ return ENOTCONN;
+ }
+ tmp = *(struct sockaddr_in6 *)(void *)nam;
+ dst = &tmp;
+ }
+
+ /*
+ * For an ICMPv6 packet, we should know its type and code
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+ if (m->m_len < sizeof(struct icmp6_hdr) &&
+ (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ icmp6 = mtod(m, struct icmp6_hdr *);
+
+ /*
+ * Allow only to send echo request and node information request
+ * See RFC 2463 for Echo Request Message format
+ */
+ if ((icmp6->icmp6_type == ICMP6_ECHO_REQUEST && icmp6->icmp6_code == 0) ||
+ (icmp6->icmp6_type == ICMP6_NI_QUERY &&
+ (icmp6->icmp6_code == ICMP6_NI_SUBJ_IPV6 ||
+ icmp6->icmp6_code == ICMP6_NI_SUBJ_FQDN))) {
+ /* Good */
+ ;
+ } else {
+ error = EPERM;
+ goto bad;
+ }
+ }
+
+#if ENABLE_DEFAULT_SCOPE
+ if (dst->sin6_scope_id == 0) { /* not change if specified */
+ dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr);
+ }