root/sys/netinet/raw_ip.c
/*      $NetBSD: raw_ip.c,v 1.187 2025/06/20 15:15:35 roy Exp $ */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982, 1986, 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)raw_ip.c    8.7 (Berkeley) 5/15/95
 */

/*
 * Raw interface to IP protocol.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: raw_ip.c,v 1.187 2025/06/20 15:15:35 roy Exp $");

#ifdef _KERNEL_OPT
#include "opt_inet.h"
#include "opt_ipsec.h"
#include "opt_mrouting.h"
#include "opt_net_mpsafe.h"
#endif

#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kauth.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_private.h>
#include <netinet/ip_mroute.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_pcb.h>
#include <netinet/in_proto.h>
#include <netinet/in_var.h>

#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif

struct inpcbtable rawcbtable;

int      rip_pcbnotify(struct inpcbtable *, struct in_addr,
    struct in_addr, int, int, void (*)(struct inpcb *, int));
static int       rip_connect_pcb(struct inpcb *, struct sockaddr_in *);
static void      rip_disconnect1(struct inpcb *);

static void sysctl_net_inet_raw_setup(struct sysctllog **);

/*
 * Nominal space allocated to a raw ip socket.
 */
#define RIPSNDQ         8192
#define RIPRCVQ         8192

static u_long           rip_sendspace = RIPSNDQ;
static u_long           rip_recvspace = RIPRCVQ;

/*
 * Raw interface to IP protocol.
 */

/*
 * Initialize raw connection block q.
 */
void
rip_init(void)
{

        sysctl_net_inet_raw_setup(NULL);
        inpcb_init(&rawcbtable, 1, 1);
}

static void
rip_sbappendaddr(struct inpcb *last, struct ip *ip, const struct sockaddr *sa,
    int hlen, struct mbuf *n)
{
        struct mbuf *opts = NULL;

        if (last->inp_flags & INP_NOHEADER)
                m_adj(n, hlen);
        if (last->inp_flags & INP_CONTROLOPTS ||
            SOOPT_TIMESTAMP(last->inp_socket->so_options))
                ip_savecontrol(last, &opts, ip, n);
        if (sbappendaddr(&last->inp_socket->so_rcv, sa, n, opts) == 0) {
                soroverflow(last->inp_socket);
                m_freem(n);
                m_freem(opts);
        } else {
                sorwakeup(last->inp_socket);
        }
}

/*
 * Setup generic address and protocol structures
 * for raw_input routine, then pass them along with
 * mbuf chain.
 */
void
rip_input(struct mbuf *m, int off, int proto)
{
        struct ip *ip = mtod(m, struct ip *);
        struct inpcb *inp;
        struct inpcb *last = NULL;
        struct mbuf *n;
        struct sockaddr_in ripsrc;
        int hlen;

        sockaddr_in_init(&ripsrc, &ip->ip_src, 0);

        /*
         * XXX Compatibility: programs using raw IP expect ip_len
         * XXX to have the header length subtracted, and in host order.
         * XXX ip_off is also expected to be host order.
         */
        hlen = ip->ip_hl << 2;
        ip->ip_len = ntohs(ip->ip_len) - hlen;
        NTOHS(ip->ip_off);

        TAILQ_FOREACH(inp, &rawcbtable.inpt_queue, inp_queue) {
                if (inp->inp_af != AF_INET)
                        continue;
                if (in4p_ip(inp).ip_p && in4p_ip(inp).ip_p != proto)
                        continue;
                if (!in_nullhost(in4p_laddr(inp)) &&
                    !in_hosteq(in4p_laddr(inp), ip->ip_dst))
                        continue;
                if (!in_nullhost(in4p_faddr(inp)) &&
                    !in_hosteq(in4p_faddr(inp), ip->ip_src))
                        continue;

                if (last == NULL) {
                        ;
                }
#if defined(IPSEC)
                else if (ipsec_used && ipsec_in_reject(m, last)) {
                        /* do not inject data into pcb */
                }
#endif
                else if ((n = m_copypacket(m, M_DONTWAIT)) != NULL) {
                        rip_sbappendaddr(last, ip, sintosa(&ripsrc), hlen, n);
                }

                last = inp;
        }

#if defined(IPSEC)
        if (ipsec_used && last != NULL && ipsec_in_reject(m, last)) {
                m_freem(m);
                IP_STATDEC(IP_STAT_DELIVERED);
                /* do not inject data into pcb */
        } else
#endif
        if (last != NULL) {
                rip_sbappendaddr(last, ip, sintosa(&ripsrc), hlen, m);
        } else if (inetsw[ip_protox[ip->ip_p]].pr_input == rip_input) {
                net_stat_ref_t ips;

                icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PROTOCOL,
                    0, 0);
                ips = IP_STAT_GETREF();
                _NET_STATINC_REF(ips, IP_STAT_NOPROTO);
                _NET_STATDEC_REF(ips, IP_STAT_DELIVERED);
                IP_STAT_PUTREF();
        } else {
                m_freem(m);
        }

        return;
}

int
rip_pcbnotify(struct inpcbtable *table,
    struct in_addr faddr, struct in_addr laddr, int proto, int errno,
    void (*notify)(struct inpcb *, int))
{
        struct inpcb *inp;
        int nmatch;

        nmatch = 0;
        TAILQ_FOREACH(inp, &table->inpt_queue, inp_queue) {
                if (inp->inp_af != AF_INET)
                        continue;
                if (in4p_ip(inp).ip_p && in4p_ip(inp).ip_p != proto)
                        continue;
                if (in_hosteq(in4p_faddr(inp), faddr) &&
                    in_hosteq(in4p_laddr(inp), laddr)) {
                        (*notify)(inp, errno);
                        nmatch++;
                }
        }

        return nmatch;
}

void *
rip_ctlinput(int cmd, const struct sockaddr *sa, void *v)
{
        struct ip *ip = v;
        void (*notify)(struct inpcb *, int) = inpcb_rtchange;
        int errno;

        if (sa->sa_family != AF_INET ||
            sa->sa_len != sizeof(struct sockaddr_in))
                return NULL;
        if ((unsigned)cmd >= PRC_NCMDS)
                return NULL;
        errno = inetctlerrmap[cmd];
        if (PRC_IS_REDIRECT(cmd))
                notify = inpcb_rtchange, ip = 0;
        else if (cmd == PRC_HOSTDEAD)
                ip = 0;
        else if (errno == 0)
                return NULL;
        if (ip) {
                rip_pcbnotify(&rawcbtable, satocsin(sa)->sin_addr,
                    ip->ip_src, ip->ip_p, errno, notify);

                /* XXX mapped address case */
        } else
                inpcb_notifyall(&rawcbtable, satocsin(sa)->sin_addr, errno,
                    notify);
        return NULL;
}

/*
 * Generate IP header and pass packet to ip_output.
 * Tack on options user may have setup with control call.
 */
int
rip_output(struct mbuf *m, struct inpcb *inp, struct mbuf *control,
    struct lwp *l)
{
        struct ip *ip;
        struct mbuf *opts;
        struct ip_pktopts pktopts;
        kauth_cred_t cred;
        int error, flags;

        flags = (inp->inp_socket->so_options & SO_DONTROUTE) |
            IP_ALLOWBROADCAST | IP_RETURNMTU;

        if (l == NULL)
                cred = NULL;
        else
                cred = l->l_cred;

        /* Setup IP outgoing packet options */
        memset(&pktopts, 0, sizeof(pktopts));
        error = ip_setpktopts(control, &pktopts, &flags, inp, cred);
        m_freem(control);
        if (error != 0)
                goto release;

        /*
         * If the user handed us a complete IP packet, use it.
         * Otherwise, allocate an mbuf for a header and fill it in.
         */
        if ((inp->inp_flags & INP_HDRINCL) == 0) {
                if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) {
                        error = EMSGSIZE;
                        goto release;
                }
                M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
                if (!m) {
                        error = ENOBUFS;
                        goto release;
                }
                ip = mtod(m, struct ip *);
                ip->ip_tos = in4p_ip(inp).ip_tos;
                ip->ip_off = htons(0);
                ip->ip_p = in4p_ip(inp).ip_p;
                ip->ip_len = htons(m->m_pkthdr.len);
                ip->ip_src = pktopts.ippo_laddr.sin_addr;
                ip->ip_dst = in4p_faddr(inp);
                ip->ip_ttl = in4p_ip(inp).ip_ttl ? in4p_ip(inp).ip_ttl : MAXTTL;
                opts = inp->inp_options;
        } else {
                if (m->m_pkthdr.len > IP_MAXPACKET) {
                        error = EMSGSIZE;
                        goto release;
                }
                if (m->m_pkthdr.len < sizeof(struct ip)) {
                        error = EINVAL;
                        goto release;
                }
                ip = mtod(m, struct ip *);

                /*
                 * If the mbuf is read-only, we need to allocate
                 * a new mbuf for the header, since we need to
                 * modify the header.
                 */
                if (M_READONLY(m)) {
                        int hlen = ip->ip_hl << 2;

                        m = m_copyup(m, hlen, (max_linkhdr + 3) & ~3);
                        if (m == NULL) {
                                error = ENOMEM;
                                goto release;
                        }
                        ip = mtod(m, struct ip *);
                }

                /* XXX userland passes ip_len and ip_off in host order */
                if (m->m_pkthdr.len != ip->ip_len) {
                        error = EINVAL;
                        goto release;
                }
                HTONS(ip->ip_len);
                HTONS(ip->ip_off);

                if (ip->ip_id != 0 || m->m_pkthdr.len < IP_MINFRAGSIZE)
                        flags |= IP_NOIPNEWID;
                opts = NULL;

                /* Prevent ip_output from overwriting header fields. */
                flags |= IP_RAWOUTPUT;

                IP_STATINC(IP_STAT_RAWOUT);
        }

        /*
         * IP output.  Note: if IP_RETURNMTU flag is set, the MTU size
         * will be stored in inp_errormtu.
         */
        return ip_output(m, opts, &inp->inp_route, flags, pktopts.ippo_imo,
            inp);

 release:
        m_freem(m);
        return error;
}

/*
 * Raw IP socket option processing.
 */
int
rip_ctloutput(int op, struct socket *so, struct sockopt *sopt)
{
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
        int optval;

        if (sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_NOHEADER) {
                if (op == PRCO_GETOPT) {
                        optval = (inp->inp_flags & INP_NOHEADER) ? 1 : 0;
                        error = sockopt_set(sopt, &optval, sizeof(optval));
                } else if (op == PRCO_SETOPT) {
                        error = sockopt_getint(sopt, &optval);
                        if (error)
                                goto out;
                        if (optval) {
                                inp->inp_flags &= ~INP_HDRINCL;
                                inp->inp_flags |= INP_NOHEADER;
                        } else
                                inp->inp_flags &= ~INP_NOHEADER;
                }
                goto out;
        } else if (sopt->sopt_level != IPPROTO_IP)
                return ip_ctloutput(op, so, sopt);

        switch (op) {

        case PRCO_SETOPT:
                switch (sopt->sopt_name) {
                case IP_HDRINCL:
                        error = sockopt_getint(sopt, &optval);
                        if (error)
                                break;
                        if (optval)
                                inp->inp_flags |= INP_HDRINCL;
                        else
                                inp->inp_flags &= ~INP_HDRINCL;
                        break;

#ifdef MROUTING
                case MRT_INIT:
                case MRT_DONE:
                case MRT_ADD_VIF:
                case MRT_DEL_VIF:
                case MRT_ADD_MFC:
                case MRT_DEL_MFC:
                case MRT_ASSERT:
                case MRT_API_CONFIG:
                case MRT_ADD_BW_UPCALL:
                case MRT_DEL_BW_UPCALL:
                        error = ip_mrouter_set(so, sopt);
                        break;
#endif

                default:
                        error = ip_ctloutput(op, so, sopt);
                        break;
                }
                break;

        case PRCO_GETOPT:
                switch (sopt->sopt_name) {
                case IP_HDRINCL:
                        optval = inp->inp_flags & INP_HDRINCL;
                        error = sockopt_set(sopt, &optval, sizeof(optval));
                        break;

#ifdef MROUTING
                case MRT_VERSION:
                case MRT_ASSERT:
                case MRT_API_SUPPORT:
                case MRT_API_CONFIG:
                        error = ip_mrouter_get(so, sopt);
                        break;
#endif

                default:
                        error = ip_ctloutput(op, so, sopt);
                        break;
                }
                break;
        }
 out:
        return error;
}

int
rip_connect_pcb(struct inpcb *inp, struct sockaddr_in *addr)
{

        if (IFNET_READER_EMPTY())
                return (EADDRNOTAVAIL);
        if (addr->sin_family != AF_INET)
                return (EAFNOSUPPORT);
        if (addr->sin_len != sizeof(*addr))
                return EINVAL;
        in4p_faddr(inp) = addr->sin_addr;
        return (0);
}

static void
rip_disconnect1(struct inpcb *inp)
{

        in4p_faddr(inp) = zeroin_addr;
}

static int
rip_attach(struct socket *so, int proto)
{
        struct inpcb *inp;
        int error;

        KASSERT(sotoinpcb(so) == NULL);
        sosetlock(so);

        if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
                error = soreserve(so, rip_sendspace, rip_recvspace);
                if (error) {
                        return error;
                }
        }

        error = inpcb_create(so, &rawcbtable);
        if (error) {
                return error;
        }
        inp = sotoinpcb(so);
        in4p_ip(inp).ip_p = proto;
        KASSERT(solocked(so));

        return 0;
}

static void
rip_detach(struct socket *so)
{
        struct inpcb *inp;

        KASSERT(solocked(so));
        inp = sotoinpcb(so);
        KASSERT(inp != NULL);

#ifdef MROUTING
        extern struct socket *ip_mrouter;
        if (so == ip_mrouter) {
                ip_mrouter_done();
        }
#endif
        inpcb_destroy(inp);
}

static int
rip_accept(struct socket *so, struct sockaddr *nam)
{
        KASSERT(solocked(so));

        panic("rip_accept");

        return EOPNOTSUPP;
}

static int
rip_bind(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
        struct inpcb *inp = sotoinpcb(so);
        struct sockaddr_in *addr = (struct sockaddr_in *)nam;
        int error = 0;
        int s, ss;
        struct ifaddr *ifa;

        KASSERT(solocked(so));
        KASSERT(inp != NULL);
        KASSERT(nam != NULL);

        if (addr->sin_len != sizeof(*addr))
                return EINVAL;

        s = splsoftnet();
        if (IFNET_READER_EMPTY()) {
                error = EADDRNOTAVAIL;
                goto release;
        }
        if (addr->sin_family != AF_INET) {
                error = EAFNOSUPPORT;
                goto release;
        }
        ss = pserialize_read_enter();
        if ((ifa = ifa_ifwithaddr(sintosa(addr))) == NULL &&
            (inp->inp_flags & INP_BINDANY) == 0 &&
            !in_nullhost(addr->sin_addr))
        {
                pserialize_read_exit(ss);
                error = EADDRNOTAVAIL;
                goto release;
        }
        if (ifa && (ifatoia(ifa))->ia4_flags & IN6_IFF_DUPLICATED) {
                pserialize_read_exit(ss);
                error = EADDRNOTAVAIL;
                goto release;
        }
        pserialize_read_exit(ss);

        in4p_laddr(inp) = addr->sin_addr;

release:
        splx(s);
        return error;
}

static int
rip_listen(struct socket *so, struct lwp *l)
{
        KASSERT(solocked(so));

        return EOPNOTSUPP;
}

static int
rip_connect(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
        int s;

        KASSERT(solocked(so));
        KASSERT(inp != NULL);
        KASSERT(nam != NULL);

        s = splsoftnet();
        error = rip_connect_pcb(inp, (struct sockaddr_in *)nam);
        if (! error)
                soisconnected(so);
        splx(s);

        return error;
}

static int
rip_connect2(struct socket *so, struct socket *so2)
{
        KASSERT(solocked(so));

        return EOPNOTSUPP;
}

static int
rip_disconnect(struct socket *so)
{
        struct inpcb *inp = sotoinpcb(so);
        int s;

        KASSERT(solocked(so));
        KASSERT(inp != NULL);

        s = splsoftnet();
        soisdisconnected(so);
        rip_disconnect1(inp);
        splx(s);

        return 0;
}

static int
rip_shutdown(struct socket *so)
{
        int s;

        KASSERT(solocked(so));

        /*
         * Mark the connection as being incapable of further input.
         */
        s = splsoftnet();
        socantsendmore(so);
        splx(s);

        return 0;
}

static int
rip_abort(struct socket *so)
{
        KASSERT(solocked(so));

        panic("rip_abort");

        return EOPNOTSUPP;
}

static int
rip_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp)
{
        return in_control(so, cmd, nam, ifp);
}

static int
rip_stat(struct socket *so, struct stat *ub)
{
        KASSERT(solocked(so));

        /* stat: don't bother with a blocksize. */
        return 0;
}

static int
rip_peeraddr(struct socket *so, struct sockaddr *nam)
{
        int s;

        KASSERT(solocked(so));
        KASSERT(sotoinpcb(so) != NULL);
        KASSERT(nam != NULL);

        s = splsoftnet();
        inpcb_fetch_peeraddr(sotoinpcb(so), (struct sockaddr_in *)nam);
        splx(s);

        return 0;
}

static int
rip_sockaddr(struct socket *so, struct sockaddr *nam)
{
        int s;

        KASSERT(solocked(so));
        KASSERT(sotoinpcb(so) != NULL);
        KASSERT(nam != NULL);

        s = splsoftnet();
        inpcb_fetch_sockaddr(sotoinpcb(so), (struct sockaddr_in *)nam);
        splx(s);

        return 0;
}

static int
rip_rcvd(struct socket *so, int flags, struct lwp *l)
{
        KASSERT(solocked(so));

        return EOPNOTSUPP;
}

static int
rip_recvoob(struct socket *so, struct mbuf *m, int flags)
{
        KASSERT(solocked(so));

        return EOPNOTSUPP;
}

static int
rip_send(struct socket *so, struct mbuf *m, struct sockaddr *nam,
    struct mbuf *control, struct lwp *l)
{
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
        int s;

        KASSERT(solocked(so));
        KASSERT(inp != NULL);
        KASSERT(m != NULL);

        /*
         * Ship a packet out.  The appropriate raw output
         * routine handles any massaging necessary.
         */
        s = splsoftnet();
        if (nam) {
                if ((so->so_state & SS_ISCONNECTED) != 0) {
                        error = EISCONN;
                        goto die;
                }
                error = rip_connect_pcb(inp, (struct sockaddr_in *)nam);
                if (error)
                        goto die;
        } else {
                if ((so->so_state & SS_ISCONNECTED) == 0) {
                        error = ENOTCONN;
                        goto die;
                }
        }
        error = rip_output(m, inp, control, l);
        m = NULL;
        control = NULL;
        if (nam)
                rip_disconnect1(inp);
 die:
        m_freem(m);
        m_freem(control);

        splx(s);
        return error;
}

static int
rip_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control)
{
        KASSERT(solocked(so));

        m_freem(m);
        m_freem(control);

        return EOPNOTSUPP;
}

static int
rip_purgeif(struct socket *so, struct ifnet *ifp)
{
        int s;

        s = splsoftnet();
        mutex_enter(softnet_lock);
        inpcb_purgeif0(&rawcbtable, ifp);
#ifdef NET_MPSAFE
        mutex_exit(softnet_lock);
#endif
        in_purgeif(ifp);
#ifdef NET_MPSAFE
        mutex_enter(softnet_lock);
#endif
        inpcb_purgeif(&rawcbtable, ifp);
        mutex_exit(softnet_lock);
        splx(s);

        return 0;
}

PR_WRAP_USRREQS(rip)
#define rip_attach      rip_attach_wrapper
#define rip_detach      rip_detach_wrapper
#define rip_accept      rip_accept_wrapper
#define rip_bind        rip_bind_wrapper
#define rip_listen      rip_listen_wrapper
#define rip_connect     rip_connect_wrapper
#define rip_connect2    rip_connect2_wrapper
#define rip_disconnect  rip_disconnect_wrapper
#define rip_shutdown    rip_shutdown_wrapper
#define rip_abort       rip_abort_wrapper
#define rip_ioctl       rip_ioctl_wrapper
#define rip_stat        rip_stat_wrapper
#define rip_peeraddr    rip_peeraddr_wrapper
#define rip_sockaddr    rip_sockaddr_wrapper
#define rip_rcvd        rip_rcvd_wrapper
#define rip_recvoob     rip_recvoob_wrapper
#define rip_send        rip_send_wrapper
#define rip_sendoob     rip_sendoob_wrapper
#define rip_purgeif     rip_purgeif_wrapper

const struct pr_usrreqs rip_usrreqs = {
        .pr_attach      = rip_attach,
        .pr_detach      = rip_detach,
        .pr_accept      = rip_accept,
        .pr_bind        = rip_bind,
        .pr_listen      = rip_listen,
        .pr_connect     = rip_connect,
        .pr_connect2    = rip_connect2,
        .pr_disconnect  = rip_disconnect,
        .pr_shutdown    = rip_shutdown,
        .pr_abort       = rip_abort,
        .pr_ioctl       = rip_ioctl,
        .pr_stat        = rip_stat,
        .pr_peeraddr    = rip_peeraddr,
        .pr_sockaddr    = rip_sockaddr,
        .pr_rcvd        = rip_rcvd,
        .pr_recvoob     = rip_recvoob,
        .pr_send        = rip_send,
        .pr_sendoob     = rip_sendoob,
        .pr_purgeif     = rip_purgeif,
};

static void
sysctl_net_inet_raw_setup(struct sysctllog **clog)
{

        sysctl_createv(clog, 0, NULL, NULL,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_NODE, "inet", NULL,
                       NULL, 0, NULL, 0,
                       CTL_NET, PF_INET, CTL_EOL);
        sysctl_createv(clog, 0, NULL, NULL,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_NODE, "raw",
                       SYSCTL_DESCR("Raw IPv4 settings"),
                       NULL, 0, NULL, 0,
                       CTL_NET, PF_INET, IPPROTO_RAW, CTL_EOL);

        sysctl_createv(clog, 0, NULL, NULL,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_STRUCT, "pcblist",
                       SYSCTL_DESCR("Raw IPv4 control block list"),
                       sysctl_inpcblist, 0, &rawcbtable, 0,
                       CTL_NET, PF_INET, IPPROTO_RAW,
                       CTL_CREATE, CTL_EOL);
}