+#if __APPLE__
+
+/*
+ * Non-privileged ICMP socket operations
+ * - send ICMP echo request
+ * - all ICMP
+ * - limited socket options
+ */
+
+#include <netinet/ip_icmp.h>
+#include <netinet/in_pcb.h>
+
+extern struct domain inetdomain;
+extern u_long rip_sendspace;
+extern u_long rip_recvspace;
+extern struct inpcbinfo ripcbinfo;
+
+int rip_abort(struct socket *);
+int rip_bind(struct socket *, struct sockaddr *, struct proc *);
+int rip_connect(struct socket *, struct sockaddr *, struct proc *);
+int rip_detach(struct socket *);
+int rip_disconnect(struct socket *);
+int rip_shutdown(struct socket *);
+
+__private_extern__ int icmp_dgram_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p);
+__private_extern__ int icmp_dgram_attach(struct socket *so, int proto, struct proc *p);
+__private_extern__ int icmp_dgram_ctloutput(struct socket *so, struct sockopt *sopt);
+
+__private_extern__ struct pr_usrreqs icmp_dgram_usrreqs = {
+ rip_abort, pru_accept_notsupp, icmp_dgram_attach, rip_bind, rip_connect,
+ pru_connect2_notsupp, in_control, rip_detach, rip_disconnect,
+ pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, icmp_dgram_send, pru_sense_null, rip_shutdown,
+ in_setsockaddr, sosend, soreceive, sopoll
+};
+
+/* Like rip_attach but without root privilege enforcement */
+__private_extern__ int
+icmp_dgram_attach(struct socket *so, int proto, struct proc *p)
+{
+ struct inpcb *inp;
+ int error, s;
+
+ inp = sotoinpcb(so);
+ if (inp)
+ panic("icmp_dgram_attach");
+
+ error = soreserve(so, rip_sendspace, rip_recvspace);
+ if (error)
+ return error;
+ s = splnet();
+ error = in_pcballoc(so, &ripcbinfo, p);
+ splx(s);
+ if (error)
+ return error;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_vflag |= INP_IPV4;
+ inp->inp_ip_p = IPPROTO_ICMP;
+ inp->inp_ip_ttl = ip_defttl;
+ return 0;
+}
+
+/*
+ * Raw IP socket option processing.
+ */
+__private_extern__ int
+icmp_dgram_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error, optval;
+
+ if (sopt->sopt_level != IPPROTO_IP)
+ return (EINVAL);
+
+ switch (sopt->sopt_name) {
+ case IP_OPTIONS:
+ case IP_HDRINCL:
+ case IP_TOS:
+ case IP_TTL:
+ case IP_RECVOPTS:
+ case IP_RECVRETOPTS:
+ case IP_RECVDSTADDR:
+ case IP_RETOPTS:
+ case IP_MULTICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ case IP_MULTICAST_VIF:
+ case IP_PORTRANGE:
+ case IP_RECVIF:
+ case IP_IPSEC_POLICY:
+#if defined(NFAITH) && NFAITH > 0
+ case IP_FAITH:
+#endif
+ case IP_STRIPHDR:
+ error = rip_ctloutput(so, sopt);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+__private_extern__ int
+icmp_dgram_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
+ struct mbuf *control, struct proc *p)
+{
+ struct ip *ip;
+ struct inpcb *inp = sotoinpcb(so);
+ int hlen;
+ struct icmp *icp;
+ struct in_ifaddr *ia = NULL;
+ int icmplen;
+
+ if ((inp->inp_flags & INP_HDRINCL) != 0) {
+ /*
+ * This is not raw IP, we liberal only for fields TOS, id and TTL
+ */
+ ip = mtod(m, struct ip *);
+
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+ /* Some sanity checks */
+ if (m->m_pkthdr.len < hlen + ICMP_MINLEN) {
+ goto bad;
+ }
+ /* Only IPv4 */
+ if (IP_VHL_V(ip->ip_vhl) != 4)
+ goto bad;
+ if (hlen < 20 || hlen > 40 || ip->ip_len != m->m_pkthdr.len ||
+ ip->ip_len > 65535)
+ goto bad;
+ /* Bogus fragments can tie up peer resources */
+ if (ip->ip_off != 0)
+ goto bad;
+ /* Allow only ICMP even for user provided IP header */
+ if (ip->ip_p != IPPROTO_ICMP)
+ goto bad;
+ /* To prevent spoofing, specified source address must be one of ours */
+ if (ip->ip_src.s_addr != INADDR_ANY) {
+ if (TAILQ_EMPTY(&in_ifaddrhead))
+ goto bad;
+ TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
+ if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_src.s_addr)
+ goto ours;
+ }
+ goto bad;
+ }
+ours:
+ /* Do not trust we got a valid checksum */
+ ip->ip_sum = 0;
+
+ icp = (struct icmp *)(((char *)m->m_data) + hlen);
+ icmplen = m->m_pkthdr.len - hlen;
+ } else {
+ if ((icmplen = m->m_pkthdr.len) < ICMP_MINLEN) {
+ goto bad;
+ }
+ icp = mtod(m, struct icmp *);
+ }
+ /*
+ * Allow only to send request types with code 0
+ */
+ if (icp->icmp_code != 0)
+ goto bad;
+ switch (icp->icmp_type) {
+ case ICMP_ECHO:
+ break;
+ case ICMP_TSTAMP:
+ if (icmplen != 20)
+ goto bad;
+ break;
+ case ICMP_MASKREQ:
+ if (icmplen != 12)
+ goto bad;
+ break;
+ default:
+ goto bad;
+ }
+ return rip_send(so, flags, m, nam, control, p);
+bad:
+ m_freem(m);
+ return EINVAL;
+}
+
+#endif /* __APPLE__ */