/*        $OpenBSD: if_pflog.c,v 1.83 2019/06/13 21:12:52 mpi Exp $        */
      /*
       * The authors of this code are John Ioannidis (ji@tla.org),
       * Angelos D. Keromytis (kermit@csd.uch.gr) and
       * Niels Provos (provos@physnet.uni-hamburg.de).
       *
       * This code was written by John Ioannidis for BSD/OS in Athens, Greece,
       * in November 1995.
       *
       * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
       * by Angelos D. Keromytis.
       *
       * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
       * and Niels Provos.
       *
       * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
       * and Niels Provos.
       * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos.
       * Copyright (c) 2002 - 2010 Henning Brauer
       *
       * Permission to use, copy, and modify this software with or without fee
       * is hereby granted, provided that this entire notice is included in
       * all copies of any software which is or includes a copy or
       * modification of this software.
       * You may use this code under the GNU public license if you so wish. Please
       * contribute changes back to the authors under this freer than GPL license
       * so that we may further the use of strong encryption without limitations to
       * all.
       *
       * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
       * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
       * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
       * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
       * PURPOSE.
       */
      
      #include "bpfilter.h"
      #include "pflog.h"
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/mbuf.h>
      #include <sys/proc.h>
      #include <sys/socket.h>
      #include <sys/stdint.h>
      #include <sys/ioctl.h>
      
      #include <net/if.h>
      #include <net/if_var.h>
      #include <net/if_types.h>
      #include <net/bpf.h>
      
      #include <netinet/in.h>
      #include <netinet/ip.h>
      #include <netinet/ip_icmp.h>
      #include <netinet/tcp.h>
      #include <netinet/udp.h>
      
      #ifdef INET6
      #include <netinet/ip6.h>
      #include <netinet/icmp6.h>
      #endif /* INET6 */
      
      #include <net/pfvar.h>
      #include <net/pfvar_priv.h>
      #include <net/if_pflog.h>
      
      #define PFLOGMTU        (32768 + MHLEN + MLEN)
      
      #ifdef PFLOGDEBUG
      #define DPRINTF(x)    do { if (pflogdebug) printf x ; } while (0)
      #else
      #define DPRINTF(x)
      #endif
      
      void        pflogattach(int);
      int        pflogifs_resize(size_t);
      int        pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
                             struct rtentry *);
      int        pflogioctl(struct ifnet *, u_long, caddr_t);
      void        pflogstart(struct ifnet *);
      int        pflog_clone_create(struct if_clone *, int);
      int        pflog_clone_destroy(struct ifnet *);
      void        pflog_bpfcopy(const void *, void *, size_t);
      
      struct if_clone        pflog_cloner =
          IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
      
      int                  npflogifs = 0;
      struct ifnet        **pflogifs = NULL;        /* for fast access */
      struct mbuf         *pflog_mhdr = NULL, *pflog_mptr = NULL;
      
      void
      pflogattach(int npflog)
      {
              if (pflog_mhdr == NULL)
                      if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL)
                              panic("pflogattach: no mbuf");
              if (pflog_mptr == NULL)
                      if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL)
                              panic("pflogattach: no mbuf");
              if_clone_attach(&pflog_cloner);
      }
      
      int
      pflogifs_resize(size_t n)
      {
              struct ifnet        **p;
              int                  i;
      
              NET_ASSERT_LOCKED();
      
              if (n > SIZE_MAX / sizeof(*p))
                      return (EINVAL);
              if (n == 0)
                      p = NULL;
              else
                      if ((p = mallocarray(n, sizeof(*p), M_DEVBUF,
                          M_NOWAIT|M_ZERO)) == NULL)
                              return (ENOMEM);
              for (i = 0; i < n; i++)
                      if (i < npflogifs)
                              p[i] = pflogifs[i];
                      else
                              p[i] = NULL;
      
              free(pflogifs, M_DEVBUF, npflogifs * sizeof(*pflogifs));
              pflogifs = p;
              npflogifs = n;
              return (0);
      }
      
      int
      pflog_clone_create(struct if_clone *ifc, int unit)
      {
              struct ifnet *ifp;
              struct pflog_softc *pflogif;
      
              pflogif = malloc(sizeof(*pflogif), M_DEVBUF, M_WAITOK|M_ZERO);
              pflogif->sc_unit = unit;
              ifp = &pflogif->sc_if;
              snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
              ifp->if_softc = pflogif;
              ifp->if_mtu = PFLOGMTU;
              ifp->if_ioctl = pflogioctl;
              ifp->if_output = pflogoutput;
              ifp->if_start = pflogstart;
              ifp->if_xflags = IFXF_CLONED;
              ifp->if_type = IFT_PFLOG;
              IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
              ifp->if_hdrlen = PFLOG_HDRLEN;
              if_attach(ifp);
              if_alloc_sadl(ifp);
      
      #if NBPFILTER > 0
              bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
      #endif
      
              NET_LOCK();
              if (unit + 1 > npflogifs && pflogifs_resize(unit + 1) != 0) {
                      NET_UNLOCK();
                      return (ENOMEM);
              }
              pflogifs[unit] = ifp;
              NET_UNLOCK();
      
              return (0);
      }
      
      int
      pflog_clone_destroy(struct ifnet *ifp)
      {
              struct pflog_softc        *pflogif = ifp->if_softc;
              int                         i;
      
              NET_LOCK();
              pflogifs[pflogif->sc_unit] = NULL;
              for (i = npflogifs; i > 0 && pflogifs[i - 1] == NULL; i--)
                      ; /* nothing */
              if (i < npflogifs)
                      pflogifs_resize(i);        /* error harmless here */
              NET_UNLOCK();
      
              if_detach(ifp);
              free(pflogif, M_DEVBUF, sizeof(*pflogif));
              return (0);
      }
      
      /*
       * Start output on the pflog interface.
       */
      void
      pflogstart(struct ifnet *ifp)
      {
              IFQ_PURGE(&ifp->if_snd);
      }
      
      int
      pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
              struct rtentry *rt)
      {
              m_freem(m);        /* drop packet */
              return (EAFNOSUPPORT);
      }
      
      int
      pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
      {
              switch (cmd) {
              case SIOCSIFFLAGS:
                      if (ifp->if_flags & IFF_UP)
                              ifp->if_flags |= IFF_RUNNING;
                      else
                              ifp->if_flags &= ~IFF_RUNNING;
                      break;
              default:
                      return (ENOTTY);
              }
      
              return (0);
      }
      
      int
      pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
          struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *trigger)
   49 {
      #if NBPFILTER > 0
              struct ifnet *ifn;
              struct pfloghdr hdr;
      
              if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL)
                      return (-1);
              if (trigger == NULL)
                      trigger = rm;
      
              if (trigger->logif >= npflogifs || (ifn = pflogifs[trigger->logif]) ==
                  NULL || !ifn->if_bpf)
                      return (0);
      
              bzero(&hdr, sizeof(hdr));
              hdr.length = PFLOG_REAL_HDRLEN;
              hdr.action = rm->action;
              hdr.reason = reason;
              memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
      
              if (am == NULL) {
   49                 hdr.rulenr = htonl(rm->nr);
                      hdr.subrulenr = -1;
              } else {
                      hdr.rulenr = htonl(am->nr);
                      hdr.subrulenr = htonl(rm->nr);
                      if (ruleset != NULL && ruleset->anchor != NULL)
                              strlcpy(hdr.ruleset, ruleset->anchor->name,
                                  sizeof(hdr.ruleset));
              }
   49         if (trigger->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done)
                      pd->lookup.done = pf_socket_lookup(pd);
   48         if (pd->lookup.done > 0) {
    1                 hdr.uid = pd->lookup.uid;
                      hdr.pid = pd->lookup.pid;
              } else {
                      hdr.uid = UID_MAX;
                      hdr.pid = NO_PID;
              }
              hdr.rule_uid = rm->cuid;
              hdr.rule_pid = rm->cpid;
              hdr.dir = pd->dir;
      
              pf_addrcpy(&hdr.saddr, &pd->nsaddr, pd->naf);
              pf_addrcpy(&hdr.daddr, &pd->ndaddr, pd->naf);
              hdr.af = pd->af;
              hdr.naf = pd->naf;
              hdr.sport = pd->nsport;
              hdr.dport = pd->ndport;
      
              ifn->if_opackets++;
              ifn->if_obytes += pd->m->m_pkthdr.len;
      
              bpf_mtap_hdr(ifn->if_bpf, (caddr_t)&hdr, PFLOG_HDRLEN, pd->m,
                  BPF_DIRECTION_OUT, pflog_bpfcopy);
      #endif
      
              return (0);
      }
      
      void
      pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
   49 {
              struct mbuf                *m, *mp, *mhdr, *mptr;
              struct pfloghdr                *pfloghdr;
              u_int                         count;
              u_char                        *dst, *mdst;
   33         int                         afto, hlen, mlen, off;
      
              struct pf_pdesc                 pd;
              struct pf_addr                 osaddr, odaddr;
              u_int16_t                 osport = 0, odport = 0;
              u_int8_t                 proto = 0;
      
              m = (struct mbuf *)src_arg;
              dst = dst_arg;
      
              mhdr = pflog_mhdr;
              mptr = pflog_mptr;
      
              if (m == NULL)
                      panic("pflog_bpfcopy got no mbuf");
      
              /* first mbuf holds struct pfloghdr */
              pfloghdr = mtod(m, struct pfloghdr *);
              afto = pfloghdr->af != pfloghdr->naf;
              count = min(m->m_len, len);
              bcopy(pfloghdr, dst, count);
              pfloghdr = (struct pfloghdr *)dst;
              dst += count;
              len -= count;
              m = m->m_next;
      
              if (len <= 0)
                      return;
      
              /* second mbuf is pkthdr */
              if (m == NULL)
                      panic("no second mbuf");
      
              /*
               * temporary mbuf will hold an ip/ip6 header and 8 bytes
               * of the protocol header
               */
              m_inithdr(mhdr);
              mhdr->m_len = 0;        /* XXX not done in m_inithdr() */
      
      #ifdef INET6
              /* offset for a new header */
   49         if (afto && pfloghdr->af == AF_INET)
                      mhdr->m_data += sizeof(struct ip6_hdr) -
                          sizeof(struct ip);
      #endif /* INET6 */
      
              mdst = mtod(mhdr, char *);
              switch (pfloghdr->af) {
              case AF_INET: {
                      struct ip        *h;
      
                      if (m->m_pkthdr.len < sizeof(*h))
                              goto copy;
                      m_copydata(m, 0, sizeof(*h), mdst);
                      h = (struct ip *)mdst;
                      hlen = h->ip_hl << 2;
   11                 if (hlen > sizeof(*h) && (m->m_pkthdr.len >= hlen))
    5                         m_copydata(m, sizeof(*h), hlen - sizeof(*h),
                                  mdst + sizeof(*h));
                      break;
                  }
      #ifdef INET6
              case AF_INET6: {
                      struct ip6_hdr        *h;
      
                      if (m->m_pkthdr.len < sizeof(*h))
                              goto copy;
                      hlen = sizeof(struct ip6_hdr);
                      m_copydata(m, 0, hlen, mdst);
                      h = (struct ip6_hdr *)mdst;
                      proto = h->ip6_nxt;
                      break;
                  }
      #endif /* INET6 */
              default:
                      /* shouldn't happen ever :-) */
                      goto copy;
              }
      
    5         if (m->m_pkthdr.len < hlen + 8 && proto != IPPROTO_NONE)
                      goto copy;
    1         else if (proto != IPPROTO_NONE) {
                      /* copy 8 bytes of the protocol header */
   43                 m_copydata(m, hlen, 8, mdst + hlen);
                      hlen += 8;
              }
      
              mhdr->m_len += hlen;
              mhdr->m_pkthdr.len = mhdr->m_len;
      
              /* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen) */
              mp = m_getptr(m, hlen, &off);
              if (mp != NULL) {
   44                 bcopy(mp, mptr, sizeof(*mptr));
                      mptr->m_data += off;
                      mptr->m_len -= off;
                      mptr->m_flags &= ~M_PKTHDR;
                      mhdr->m_next = mptr;
                      mhdr->m_pkthdr.len += m->m_pkthdr.len - hlen;
              }
      
              /*
               * Rewrite addresses if needed. Reason pointer must be NULL to avoid
               * counting the packet here again.
               */
   33         if (pf_setup_pdesc(&pd, pfloghdr->af, pfloghdr->dir, NULL,
                  mhdr, NULL) != PF_PASS)
                      goto copy;
              pd.naf = pfloghdr->naf;
      
              pf_addrcpy(&osaddr, pd.src, pd.af);
              pf_addrcpy(&odaddr, pd.dst, pd.af);
    8         if (pd.sport)
    3                 osport = *pd.sport;
    8         if (pd.dport)
    3                 odport = *pd.dport;
      
   11         if (pd.virtual_proto != PF_VPROTO_FRAGMENT &&
                  (pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
                  pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
                  pfloghdr->dir))) {
                      m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen),
                          &pd.hdr, M_NOWAIT);
      #ifdef INET6
                      if (afto) {
                              pf_addrcpy(&pd.nsaddr, &pfloghdr->saddr, pd.naf);
                              pf_addrcpy(&pd.ndaddr, &pfloghdr->daddr, pd.naf);
                      }
      #endif /* INET6 */
                      pf_addrcpy(&pfloghdr->saddr, &osaddr, pd.af);
                      pf_addrcpy(&pfloghdr->daddr, &odaddr, pd.af);
                      pfloghdr->sport = osport;
                      pfloghdr->dport = odport;
              }
      
              pd.tot_len = min(pd.tot_len, len);
              pd.tot_len -= pd.m->m_data - pd.m->m_pktdat;
      
      #ifdef INET6
   11         if (afto && pfloghdr->rewritten)
                      pf_translate_af(&pd);
      #endif /* INET6 */
      
              m = pd.m;
       copy:
              mlen = min(m->m_pkthdr.len, len);
              m_copydata(m, 0, mlen, dst);
              len -= mlen;
   49         if (len > 0)
                      bzero(dst + mlen, len);
      }
      /*        $OpenBSD: socketvar.h,v 1.89 2019/02/15 05:55:21 dlg Exp $        */
      /*        $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $        */
      
      /*-
       * Copyright (c) 1982, 1986, 1990, 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.
       *
       *        @(#)socketvar.h        8.1 (Berkeley) 6/2/93
       */
      
      #include <sys/selinfo.h>                        /* for struct selinfo */
      #include <sys/queue.h>
      #include <sys/sigio.h>                                /* for struct sigio_ref */
      #include <sys/task.h>
      #include <sys/timeout.h>
      
      #ifndef        _SOCKLEN_T_DEFINED_
      #define        _SOCKLEN_T_DEFINED_
      typedef        __socklen_t        socklen_t;        /* length type for network syscalls */
      #endif
      
      TAILQ_HEAD(soqhead, socket);
      
      /*
       * Kernel structure per socket.
       * Contains send and receive buffer queues,
       * handle on protocol and pointer to protocol
       * private data and error information.
       */
      struct socket {
              const struct protosw *so_proto;        /* protocol handle */
              void        *so_pcb;                /* protocol control block */
              u_int        so_state;                /* internal state flags SS_*, below */
              short        so_type;                /* generic type, see socket.h */
              short        so_options;                /* from socket call, see socket.h */
              short        so_linger;                /* time to linger while closing */
      /*
       * Variables for connection queueing.
       * Socket where accepts occur is so_head in all subsidiary sockets.
       * If so_head is 0, socket is not related to an accept.
       * For head socket so_q0 queues partially completed connections,
       * while so_q is a queue of connections ready to be accepted.
       * If a connection is aborted and it has so_head set, then
       * it has to be pulled out of either so_q0 or so_q.
       * We allow connections to queue up based on current queue lengths
       * and limit on number of queued connections for this socket.
       */
              struct        socket        *so_head;        /* back pointer to accept socket */
              struct        soqhead        *so_onq;        /* queue (q or q0) that we're on */
              struct        soqhead        so_q0;                /* queue of partial connections */
              struct        soqhead        so_q;                /* queue of incoming connections */
              struct        sigio_ref so_sigio;        /* async I/O registration */
              TAILQ_ENTRY(socket) so_qe;        /* our queue entry (q or q0) */
              short        so_q0len;                /* partials on so_q0 */
              short        so_qlen;                /* number of connections on so_q */
              short        so_qlimit;                /* max number queued connections */
              short        so_timeo;                /* connection timeout */
              u_long        so_oobmark;                /* chars to oob mark */
              u_int        so_error;                /* error affecting connection */
      /*
       * Variables for socket splicing, allocated only when needed.
       */
              struct sosplice {
                      struct        socket *ssp_socket;        /* send data to drain socket */
                      struct        socket *ssp_soback;        /* back ref to source socket */
                      off_t        ssp_len;                /* number of bytes spliced */
                      off_t        ssp_max;                /* maximum number of bytes */
                      struct        timeval ssp_idletv;        /* idle timeout */
                      struct        timeout ssp_idleto;
                      struct        task ssp_task;                /* task for somove */
              } *so_sp;
      /*
       * Variables for socket buffering.
       */
              struct        sockbuf {
      /* The following fields are all zeroed on flush. */
      #define        sb_startzero        sb_cc
                      u_long        sb_cc;                /* actual chars in buffer */
                      u_long        sb_datacc;        /* data only chars in buffer */
                      u_long        sb_hiwat;        /* max actual char count */
                      u_long  sb_wat;                /* default watermark */
                      u_long        sb_mbcnt;        /* chars of mbufs used */
                      u_long        sb_mbmax;        /* max chars of mbufs to use */
                      long        sb_lowat;        /* low water mark */
                      struct mbuf *sb_mb;        /* the mbuf chain */
                      struct mbuf *sb_mbtail;        /* the last mbuf in the chain */
                      struct mbuf *sb_lastrecord;/* first mbuf of last record in
                                                    socket buffer */
                      u_short        sb_timeo;        /* timeout for read/write */
                      short        sb_flags;        /* flags, see below */
      /* End area that is zeroed on flush. */
      #define        sb_endzero        sb_flags
                      int        sb_flagsintr;        /* flags, changed atomically */
                      struct        selinfo sb_sel;        /* process selecting read/write */
              } so_rcv, so_snd;
      #define        SB_MAX                (2*1024*1024)        /* default for max chars in sockbuf */
      #define        SB_LOCK                0x01                /* lock on data queue */
      #define        SB_WANT                0x02                /* someone is waiting to lock */
      #define        SB_WAIT                0x04                /* someone is waiting for data/space */
      #define        SB_SEL                0x08                /* someone is selecting */
      #define        SB_ASYNC        0x10                /* ASYNC I/O, need signals */
      #define        SB_SPLICE        0x20                /* buffer is splice source or drain */
      #define        SB_NOINTR        0x40                /* operations not interruptible */
      #define        SB_KNOTE        0x80                /* kernel note attached */
      
              void        (*so_upcall)(struct socket *so, caddr_t arg, int waitf);
              caddr_t        so_upcallarg;                /* Arg for above */
              uid_t        so_euid, so_ruid;        /* who opened the socket */
              gid_t        so_egid, so_rgid;
              pid_t        so_cpid;                /* pid of process that opened socket */
      };
      
      /*
       * Socket state bits.
       */
      #define        SS_NOFDREF                0x001        /* no file table ref any more */
      #define        SS_ISCONNECTED                0x002        /* socket connected to a peer */
      #define        SS_ISCONNECTING                0x004        /* in process of connecting to peer */
      #define        SS_ISDISCONNECTING        0x008        /* in process of disconnecting */
      #define        SS_CANTSENDMORE                0x010        /* can't send more data to peer */
      #define        SS_CANTRCVMORE                0x020        /* can't receive more data from peer */
      #define        SS_RCVATMARK                0x040        /* at mark on input */
      #define        SS_ISDISCONNECTED        0x800        /* socket disconnected from peer */
      
      #define        SS_PRIV                        0x080        /* privileged for broadcast, raw... */
      #define        SS_ASYNC                0x200        /* async i/o notify */
      #define        SS_CONNECTOUT                0x1000        /* connect, not accept, at this end */
      #define        SS_ISSENDING                0x2000        /* hint for lower layer */
      #define        SS_DNS                        0x4000        /* created using SOCK_DNS socket(2) */
      
      #ifdef _KERNEL
      
      #include <lib/libkern/libkern.h>
      
      /*
       * Values for sounlock()/sofree().
       */
      #define SL_NOUNLOCK        0x00
      #define SL_LOCKED        0x42
      
      void        soassertlocked(struct socket *);
      
      /*
       * Macros for sockets and socket buffering.
       */
      
      #define isspliced(so)                ((so)->so_sp && (so)->so_sp->ssp_socket)
      #define issplicedback(so)        ((so)->so_sp && (so)->so_sp->ssp_soback)
      
      /*
       * Do we need to notify the other side when I/O is possible?
       */
      static inline int
      sb_notify(struct socket *so, struct sockbuf *sb)
      {
              int flags = (sb->sb_flags | sb->sb_flagsintr);
      
              KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
              soassertlocked(so);
              return ((flags & (SB_WAIT|SB_SEL|SB_ASYNC|SB_SPLICE|SB_KNOTE)) != 0);
      }
      
      /*
       * How much space is there in a socket buffer (so->so_snd or so->so_rcv)?
       * This is problematical if the fields are unsigned, as the space might
       * still be negative (cc > hiwat or mbcnt > mbmax).  Should detect
       * overflow and return 0.
       */
      static inline long
      sbspace(struct socket *so, struct sockbuf *sb)
      {
              KASSERT(sb == &so->so_rcv || sb == &so->so_snd);
              return lmin(sb->sb_hiwat - sb->sb_cc, sb->sb_mbmax - sb->sb_mbcnt);
      }
      
      /* do we have to send all at once on a socket? */
      #define        sosendallatonce(so) \
          ((so)->so_proto->pr_flags & PR_ATOMIC)
      
      /* are we sending on this socket? */
      #define        soissending(so) \
          ((so)->so_state & SS_ISSENDING)
      
      /* can we read something from so? */
      static inline int
      soreadable(struct socket *so)
      {
              soassertlocked(so);
   51         if (isspliced(so))
                      return 0;
   26         return (so->so_state & SS_CANTRCVMORE) || so->so_qlen || so->so_error ||
                  so->so_rcv.sb_cc >= so->so_rcv.sb_lowat;
      }
      
      /* can we write something to so? */
      #define        sowriteable(so) \
          ((sbspace((so), &(so)->so_snd) >= (so)->so_snd.sb_lowat && \
              (((so)->so_state & SS_ISCONNECTED) || \
                ((so)->so_proto->pr_flags & PR_CONNREQUIRED)==0)) || \
          ((so)->so_state & SS_CANTSENDMORE) || (so)->so_error)
      
      /* adjust counters in sb reflecting allocation of m */
      #define        sballoc(sb, m) do {                                                \
              (sb)->sb_cc += (m)->m_len;                                        \
              if ((m)->m_type != MT_CONTROL && (m)->m_type != MT_SONAME)        \
                      (sb)->sb_datacc += (m)->m_len;                                \
              (sb)->sb_mbcnt += MSIZE;                                        \
              if ((m)->m_flags & M_EXT)                                        \
                      (sb)->sb_mbcnt += (m)->m_ext.ext_size;                        \
      } while (/* CONSTCOND */ 0)
      
      /* adjust counters in sb reflecting freeing of m */
      #define        sbfree(sb, m) do {                                                \
              (sb)->sb_cc -= (m)->m_len;                                        \
              if ((m)->m_type != MT_CONTROL && (m)->m_type != MT_SONAME)        \
                      (sb)->sb_datacc -= (m)->m_len;                                \
              (sb)->sb_mbcnt -= MSIZE;                                        \
              if ((m)->m_flags & M_EXT)                                        \
                      (sb)->sb_mbcnt -= (m)->m_ext.ext_size;                        \
      } while (/* CONSTCOND */ 0)
      
      /*
       * Set lock on sockbuf sb; sleep if lock is already held.
       * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
       * Returns error without lock if sleep is interrupted.
       */
      int sblock(struct socket *, struct sockbuf *, int);
      
      /* release lock on sockbuf sb */
      void sbunlock(struct socket *, struct sockbuf *);
      
      #define        SB_EMPTY_FIXUP(sb) do {                                                \
              if ((sb)->sb_mb == NULL) {                                        \
                      (sb)->sb_mbtail = NULL;                                        \
                      (sb)->sb_lastrecord = NULL;                                \
              }                                                                \
      } while (/*CONSTCOND*/0)
      
      extern u_long sb_max;
      
      extern struct pool        socket_pool;
      
      struct mbuf;
      struct sockaddr;
      struct proc;
      struct msghdr;
      struct stat;
      struct knote;
      
      /*
       * File operations on sockets.
       */
      int        soo_read(struct file *, struct uio *, int);
      int        soo_write(struct file *, struct uio *, int);
      int        soo_ioctl(struct file *, u_long, caddr_t, struct proc *);
      int        soo_poll(struct file *, int events, struct proc *);
      int        soo_kqfilter(struct file *, struct knote *);
      int         soo_close(struct file *, struct proc *);
      int        soo_stat(struct file *, struct stat *, struct proc *);
      void        sbappend(struct socket *, struct sockbuf *, struct mbuf *);
      void        sbappendstream(struct socket *, struct sockbuf *, struct mbuf *);
      int        sbappendaddr(struct socket *, struct sockbuf *,
                  const struct sockaddr *, struct mbuf *, struct mbuf *);
      int        sbappendcontrol(struct socket *, struct sockbuf *, struct mbuf *,
                  struct mbuf *);
      void        sbappendrecord(struct socket *, struct sockbuf *, struct mbuf *);
      void        sbcompress(struct sockbuf *sb, struct mbuf *m, struct mbuf *n);
      struct mbuf *
              sbcreatecontrol(const void *, size_t, int type, int level);
      void        sbdrop(struct socket *, struct sockbuf *, int);
      void        sbdroprecord(struct sockbuf *sb);
      void        sbflush(struct socket *, struct sockbuf *);
      void        sbinsertoob(struct sockbuf *sb, struct mbuf *m0);
      void        sbrelease(struct socket *, struct sockbuf *);
      int        sbcheckreserve(u_long cnt, u_long defcnt);
      int        sbchecklowmem(void);
      int        sbreserve(struct socket *, struct sockbuf *, u_long);
      int        sbwait(struct socket *, struct sockbuf *sb);
      int        sb_lock(struct sockbuf *sb);
      void        soinit(void);
      int        soabort(struct socket *so);
      int        soaccept(struct socket *so, struct mbuf *nam);
      int        sobind(struct socket *so, struct mbuf *nam, struct proc *p);
      void        socantrcvmore(struct socket *so);
      void        socantsendmore(struct socket *so);
      int        soclose(struct socket *, int);
      int        soconnect(struct socket *so, struct mbuf *nam);
      int        soconnect2(struct socket *so1, struct socket *so2);
      int        socreate(int dom, struct socket **aso, int type, int proto);
      int        sodisconnect(struct socket *so);
      void        sofree(struct socket *so, int);
      int        sogetopt(struct socket *so, int level, int optname, struct mbuf *m);
      void        sohasoutofband(struct socket *so);
      void        soisconnected(struct socket *so);
      void        soisconnecting(struct socket *so);
      void        soisdisconnected(struct socket *so);
      void        soisdisconnecting(struct socket *so);
      int        solisten(struct socket *so, int backlog);
      struct socket *sonewconn(struct socket *head, int connstatus);
      void        soqinsque(struct socket *head, struct socket *so, int q);
      int        soqremque(struct socket *so, int q);
      int        soreceive(struct socket *so, struct mbuf **paddr, struct uio *uio,
                  struct mbuf **mp0, struct mbuf **controlp, int *flagsp,
                  socklen_t controllen);
      int        soreserve(struct socket *so, u_long sndcc, u_long rcvcc);
      void        sorflush(struct socket *so);
      int        sosend(struct socket *so, struct mbuf *addr, struct uio *uio,
                  struct mbuf *top, struct mbuf *control, int flags);
      int        sosetopt(struct socket *so, int level, int optname, struct mbuf *m);
      int        soshutdown(struct socket *so, int how);
      void        sowakeup(struct socket *so, struct sockbuf *sb);
      void        sorwakeup(struct socket *);
      void        sowwakeup(struct socket *);
      int        sockargs(struct mbuf **, const void *, size_t, int);
      
      int        sosleep(struct socket *, void *, int, const char *, int);
      int        solock(struct socket *);
      void        sounlock(struct socket *, int);
      
      int        sendit(struct proc *, int, struct msghdr *, int, register_t *);
      int        recvit(struct proc *, int, struct msghdr *, caddr_t,
                          register_t *);
      int        doaccept(struct proc *, int, struct sockaddr *, socklen_t *, int,
                  register_t *);
      
      #ifdef SOCKBUF_DEBUG
      void        sblastrecordchk(struct sockbuf *, const char *);
      #define        SBLASTRECORDCHK(sb, where)        sblastrecordchk((sb), (where))
      
      void        sblastmbufchk(struct sockbuf *, const char *);
      #define        SBLASTMBUFCHK(sb, where)        sblastmbufchk((sb), (where))
      void        sbcheck(struct sockbuf *sb);
      #define        SBCHECK(sb)                        sbcheck(sb)
      #else
      #define        SBLASTRECORDCHK(sb, where)        /* nothing */
      #define        SBLASTMBUFCHK(sb, where)        /* nothing */
      #define        SBCHECK(sb)                        /* nothing */
      #endif /* SOCKBUF_DEBUG */
      
      #endif /* _KERNEL */
      /*        $OpenBSD: bio.c,v 1.17 2015/08/26 22:28:57 deraadt Exp $        */
      
      /*
       * Copyright (c) 2002 Niklas Hallqvist.  All rights reserved.
       * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>.  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.
       *
       * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
       */
      
      /* A device controller ioctl tunnelling device.  */
      
      #include <sys/param.h>
      #include <sys/device.h>
      #include <sys/ioctl.h>
      #include <sys/malloc.h>
      #include <sys/queue.h>
      #include <sys/systm.h>
      
      #include <dev/biovar.h>
      
      struct bio_mapping {
              LIST_ENTRY(bio_mapping) bm_link;
              struct device *bm_dev;
              int (*bm_ioctl)(struct device *, u_long, caddr_t);
      };
      
      LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
      
      void        bioattach(int);
      int        bioclose(dev_t, int, int, struct proc *);
      int        bioioctl(dev_t, u_long, caddr_t, int, struct proc *);
      int        bioopen(dev_t, int, int, struct proc *);
      
      int        bio_delegate_ioctl(struct bio_mapping *, u_long, caddr_t);
      struct        bio_mapping *bio_lookup(char *);
      int        bio_validate(void *);
      
      void
      bioattach(int nunits)
      {
      }
      
      int
      bioopen(dev_t dev, int flags, int mode, struct proc *p)
    1 {
              return (0);
      }
      
      int
      bioclose(dev_t dev, int flags, int mode, struct proc *p)
    1 {
              return (0);
      }
      
      int
      bioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
   15 {
              struct bio_locate *locate;
              struct bio *bio;
              char name[16];
              int error;
      
   11         switch (cmd) {
              case BIOCLOCATE:
                      locate = (struct bio_locate *)addr;
                      error = copyinstr(locate->bl_name, name, sizeof name, NULL);
    2                 if (error != 0)
                              return (error);
    2                 locate->bl_bio.bio_cookie = bio_lookup(name);
                      if (locate->bl_bio.bio_cookie == NULL)
                              return (ENOENT);
                      break;
      
              case BIOCINQ:
              case BIOCDISK:
              case BIOCVOL:
              case BIOCALARM:
              case BIOCBLINK:
              case BIOCSETSTATE:
              case BIOCCREATERAID:
              case BIOCDELETERAID:
              case BIOCDISCIPLINE:
              case BIOCPATROL:
                      bio = (struct bio *)addr;
   10                 if (!bio_validate(bio->bio_cookie))
                              return (ENOENT);
                      return (bio_delegate_ioctl(
                          (struct bio_mapping *)bio->bio_cookie, cmd, addr));
      
              default:
                      return (ENXIO);
              }
              return (0);
      }
      
      int
      bio_register(struct device *dev, int (*ioctl)(struct device *, u_long, caddr_t))
      {
              struct bio_mapping *bm;
      
              bm = malloc(sizeof *bm, M_DEVBUF, M_NOWAIT);
              if (bm == NULL)
                      return (ENOMEM);
              bm->bm_dev = dev;
              bm->bm_ioctl = ioctl;
              LIST_INSERT_HEAD(&bios, bm, bm_link);
              return (0);
      }
      
      void
      bio_unregister(struct device *dev)
      {
              struct bio_mapping *bm, *next;
      
              for (bm = LIST_FIRST(&bios); bm != NULL; bm = next) {
                      next = LIST_NEXT(bm, bm_link);
      
                      if (dev == bm->bm_dev) {
                              LIST_REMOVE(bm, bm_link);
                              free(bm, M_DEVBUF, sizeof(*bm));
                      }
              }
      }
      
      struct bio_mapping *
      bio_lookup(char *name)
      {
              struct bio_mapping *bm;
      
    2         LIST_FOREACH(bm, &bios, bm_link)
                      if (strcmp(name, bm->bm_dev->dv_xname) == 0)
                              return (bm);
              return (NULL);
      }
      
      int
      bio_validate(void *cookie)
      {
              struct bio_mapping *bm;
      
   10         LIST_FOREACH(bm, &bios, bm_link)
                      if (bm == cookie)
                              return (1);
              return (0);
      }
      
      int
      bio_delegate_ioctl(struct bio_mapping *bm, u_long cmd, caddr_t addr)
      {
              return (bm->bm_ioctl(bm->bm_dev, cmd, addr));
      }
      
      void
      bio_info(struct bio_status *bs, int print, const char *fmt, ...)
      {
              va_list        ap;
      
              va_start(ap, fmt);
              bio_status(bs, print, BIO_MSG_INFO, fmt, &ap);
              va_end(ap);
      }
      
      void
      bio_warn(struct bio_status *bs, int print, const char *fmt, ...)
      {
              va_list        ap;
      
              va_start(ap, fmt);
              bio_status(bs, print, BIO_MSG_WARN, fmt, &ap);
              va_end(ap);
      }
      
      void
      bio_error(struct bio_status *bs, int print, const char *fmt, ...)
      {
              va_list        ap;
      
              va_start(ap, fmt);
              bio_status(bs, print, BIO_MSG_ERROR, fmt, &ap);
              va_end(ap);
      }
      
      void
      bio_status_init(struct bio_status *bs, struct device *dv)
      {
              bzero(bs, sizeof(struct bio_status));
      
              bs->bs_status = BIO_STATUS_UNKNOWN;
      
              strlcpy(bs->bs_controller, dv->dv_xname, sizeof(bs->bs_controller));
      }
      
      void
      bio_status(struct bio_status *bs, int print, int msg_type, const char *fmt,
          va_list *ap)
      {
              int idx;
      
              if (bs->bs_msg_count >= BIO_MSG_COUNT) {
                      printf("%s: insufficient message buffers\n", bs->bs_controller);
                      return;
              }
      
              idx = bs->bs_msg_count++;
      
              bs->bs_msgs[idx].bm_type = msg_type;
              vsnprintf(bs->bs_msgs[idx].bm_msg, BIO_MSG_LEN, fmt, *ap);
      
              if (print)
                      printf("%s: %s\n", bs->bs_controller, bs->bs_msgs[idx].bm_msg);
      }
      /* $OpenBSD: vfs_getcwd.c,v 1.36 2019/05/30 13:34:54 beck Exp $ */
      /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
      
      /*
       * Copyright (c) 1999 The NetBSD Foundation, Inc.
       * All rights reserved.
       *
       * This code is derived from software contributed to The NetBSD Foundation
       * by Bill Sommerfeld.
       *
       * 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.
       *
       * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/namei.h>
      #include <sys/filedesc.h>
      #include <sys/kernel.h>
      #include <sys/stat.h>
      #include <sys/lock.h>
      #include <sys/vnode.h>
      #include <sys/mount.h>
      #include <sys/ktrace.h>
      #include <sys/proc.h>
      #include <sys/uio.h>
      #include <sys/malloc.h>
      #include <sys/dirent.h>
      #include <ufs/ufs/dir.h>        /* only for DIRBLKSIZ */
      
      #include <sys/syscallargs.h>
      
      
      /* Find parent vnode of *lvpp, return in *uvpp */
      int
      vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
          char *bufp, struct proc *p)
    5 {
              int eofflag, tries, dirbuflen = 0, len, reclen, error = 0;
              off_t off;
              struct uio uio;
              struct iovec iov;
              char *dirbuf = NULL;
              ino_t fileno;
              struct vattr va;
              struct vnode *uvp = NULL;
              struct vnode *lvp = *lvpp;        
              struct componentname cn;
      
              tries = 0;
      
              /*
               * If we want the filename, get some info we need while the
               * current directory is still locked.
               */
    5         if (bufp != NULL) {
                      error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
                      if (error) {
                              vput(lvp);
                              *lvpp = NULL;
                              *uvpp = NULL;
                              return (error);
                      }
              }
      
              cn.cn_nameiop = LOOKUP;
              cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
              cn.cn_proc = p;
              cn.cn_cred = p->p_ucred;
              cn.cn_pnbuf = NULL;
              cn.cn_nameptr = "..";
              cn.cn_namelen = 2;
              cn.cn_consume = 0;
      
              /* Get parent vnode using lookup of '..' */
              error = VOP_LOOKUP(lvp, uvpp, &cn);
              if (error) {
    2                 vput(lvp);
                      *lvpp = NULL;
                      *uvpp = NULL;
                      return (error);
              }
      
              uvp = *uvpp;
      
              /* If we don't care about the pathname, we're done */
    3         if (bufp == NULL) {
                      error = 0;
                      goto out;
              }
      
              fileno = va.va_fileid;
      
              dirbuflen = DIRBLKSIZ;
              if (dirbuflen < va.va_blocksize)
                      dirbuflen = va.va_blocksize;
              /* XXX we need some limit for fuse, 1 MB should be enough */
              if (dirbuflen > 0xfffff) {
                      error = EINVAL;
                      goto out;
              }
              dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
      
              off = 0;
      
              do {
                      char   *cpos;
                      struct dirent *dp;
      
                      iov.iov_base = dirbuf;
                      iov.iov_len = dirbuflen;
      
                      uio.uio_iov = &iov;
                      uio.uio_iovcnt = 1;
                      uio.uio_offset = off;
                      uio.uio_resid = dirbuflen;
                      uio.uio_segflg = UIO_SYSSPACE;
                      uio.uio_rw = UIO_READ;
                      uio.uio_procp = p;
      
                      eofflag = 0;
      
                      /* Call VOP_READDIR of parent */
                      error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);
      
                      off = uio.uio_offset;
      
                      /* Try again if NFS tosses its cookies */
                      if (error == EINVAL && tries < 3) {
                              tries++;
                              off = 0;
                              continue;
                      } else if (error) {
                              goto out; /* Old userland getcwd() behaviour */
                      }
      
                      cpos = dirbuf;
                      tries = 0;
      
                      /* Scan directory page looking for matching vnode */ 
                      for (len = (dirbuflen - uio.uio_resid); len > 0;
                           len -= reclen) {
                              dp = (struct dirent *)cpos;
                              reclen = dp->d_reclen;
      
                              /* Check for malformed directory */
                              if (reclen < DIRENT_RECSIZE(1) || reclen > len) {
                                      error = EINVAL;
                                      goto out;
                              }
      
                              if (dp->d_fileno == fileno) {
                                      char *bp = *bpp;
      
                                      if (offsetof(struct dirent, d_name) +
                                          dp->d_namlen > reclen) {
                                              error = EINVAL;
                                              goto out;
                                      }
                                      bp -= dp->d_namlen;
                                      if (bp <= bufp) {
                                              error = ERANGE;
                                              goto out;
                                      }
      
                                      memmove(bp, dp->d_name, dp->d_namlen);
                                      error = 0;
                                      *bpp = bp;
      
                                      goto out;
                              }
      
                              cpos += reclen;
                      }
      
              } while (!eofflag);
      
              error = ENOENT;
      
      out:
      
              vrele(lvp);
              *lvpp = NULL;
      
              free(dirbuf, M_TEMP, dirbuflen);
      
              return (error);
      }
      
      /* Do a lookup in the vnode-to-name reverse */
      int
      vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
          char *bufp)
    5 {
              struct vnode *lvp, *uvp = NULL;
              char *obp;
              int error, vpid;
      
              lvp = *lvpp;
              obp = *bpp;        /* Save original position to restore to on error */
      
              error = cache_revlookup(lvp, uvpp, bpp, bufp);
    5         if (error) {
                      if (error != -1) {
                              vput(lvp);
                              *lvpp = NULL;
                              *uvpp = NULL;
                      }
      
                      return (error);
              }
      
              uvp = *uvpp;
              vpid = uvp->v_id;
      
      
              /* Release current lock before acquiring the parent lock */
              VOP_UNLOCK(lvp);
      
              error = vget(uvp, LK_EXCLUSIVE | LK_RETRY);
              if (error)
                      *uvpp = NULL;
      
              /*
               * Verify that vget() succeeded, and check that vnode capability
               * didn't change while we were waiting for the lock.
               */
    3         if (error || (vpid != uvp->v_id)) {
                      /*
                       * Try to get our lock back. If that works, tell the caller to
                       * try things the hard way, otherwise give up.
                       */
                      if (!error)
                              vput(uvp);
      
                      *uvpp = NULL;
                      
                      error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
                      if (!error) {
                              *bpp = obp; /* restore the buffer */
                              return (-1);
                      }
              }
      
              vrele(lvp);
              *lvpp = NULL;
      
              return (error);
      }
      
      /* Common routine shared by sys___getcwd() and vn_isunder() and sys___realpath() */
      int
    5 vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
          int limit, int flags, struct proc *p)
    6 {
              struct filedesc *fdp = p->p_fd;
              struct vnode *uvp = NULL;
              char *bp = NULL;
              int error, perms = VEXEC;
      
    6         if (rvp == NULL) {
                      rvp = fdp->fd_rdir;
                      if (rvp == NULL)
                              rvp = rootvnode;
              }
      
              vref(rvp);
              vref(lvp);
      
              error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
              if (error) {
                      vrele(lvp);
                      lvp = NULL;
                      goto out;
              }
      
    6         if (bufp)
                      bp = *bpp;
      
              if (lvp == rvp) {
    1                 if (bp)
                              *(--bp) = '/';
                      goto out;
              }
      
              /*
               * This loop will terminate when we hit the root, VOP_READDIR() or
               * VOP_LOOKUP() fails, or we run out of space in the user buffer.
               */
              do {
                      if (lvp->v_type != VDIR) {
                              error = ENOTDIR;
                              goto out;
                      }
      
                      /* Check for access if caller cares */
    5                 if (flags & GETCWD_CHECK_ACCESS) {
                              error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
                              if (error)
                                      goto out;
                              perms = VEXEC|VREAD;
                      }
      
                      /* Step up if we're a covered vnode */
                      while (lvp->v_flag & VROOT) {
                              struct vnode *tvp;
      
                              if (lvp == rvp)
                                      goto out;
                              
                              tvp = lvp;
                              lvp = lvp->v_mount->mnt_vnodecovered;
      
                              vput(tvp);
      
    1                         if (lvp == NULL) {
                                      error = ENOENT;
                                      goto out;
                              }
      
                              vref(lvp);
      
                              error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
                              if (error) {
                                      vrele(lvp);
                                      lvp = NULL;
                                      goto out;
                              }
                      }
      
                      /* Look in the name cache */
                      error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
      
    3                 if (error == -1) {
                              /* If that fails, look in the directory */
                              error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
                      }
      
    2                 if (error)
                              goto out;
      
      #ifdef DIAGNOSTIC
                      if (lvp != NULL)
                              panic("getcwd: oops, forgot to null lvp");
                      if (bufp && (bp <= bufp)) {
                              panic("getcwd: oops, went back too far");
                      }
      #endif
      
    3                 if (bp)
                              *(--bp) = '/';
      
                      lvp = uvp;
                      uvp = NULL;
                      limit--;
      
    3         } while ((lvp != rvp) && (limit > 0)); 
      
      out:
      
    6         if (bpp)
                      *bpp = bp;
      
    6         if (uvp)
                      vput(uvp);
      
    3         if (lvp)
    3                 vput(lvp);
      
              vrele(rvp);
      
              return (error);
      }
      
      /* Find pathname of a process's current directory */
      int
      sys___getcwd(struct proc *p, void *v, register_t *retval) 
      {
              struct sys___getcwd_args *uap = v;
              int error, len = SCARG(uap, len);
              char *path, *bp;
      
              if (len > MAXPATHLEN * 4)
                      len = MAXPATHLEN * 4;
              else if (len < 2)
                      return (ERANGE);
      
              path = malloc(len, M_TEMP, M_WAITOK);
      
              bp = &path[len - 1];
              *bp = '\0';
      
              /*
               * 5th argument here is "max number of vnodes to traverse".
               * Since each entry takes up at least 2 bytes in the output
               * buffer, limit it to N/2 vnodes for an N byte buffer.
               */
              error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
                  GETCWD_CHECK_ACCESS, p);
      
              if (error)
                      goto out;
      
              /* Put the result into user buffer */
              error = copyoutstr(bp, SCARG(uap, buf), MAXPATHLEN, NULL);
      
      #ifdef KTRACE
              if (KTRPOINT(p, KTR_NAMEI))
                      ktrnamei(p, bp);
      #endif
      
      out:
              free(path, M_TEMP, len);
      
              return (error);
      }
      /*        $OpenBSD: subr_poison.c,v 1.14 2017/09/08 05:36:53 deraadt Exp $ */
      /*
       * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
       *
       * Permission to use, copy, modify, and distribute this software for any
       * purpose with or without fee is hereby granted, provided that the above
       * copyright notice and this permission notice appear in all copies.
       *
       * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       */
      
      #include <sys/param.h>
      
      #include <uvm/uvm_extern.h>
      
      /*
       * The POISON is used as known text to copy into free objects so
       * that modifications after frees can be detected.
       */
      #ifdef DEADBEEF0
      #define POISON0        ((unsigned) DEADBEEF0)
      #else
      #define POISON0        ((unsigned) 0xdeadbeef)
      #endif
      #ifdef DEADBEEF1
      #define POISON1        ((unsigned) DEADBEEF1)
      #else
      #define POISON1        ((unsigned) 0xdeafbead)
      #endif
      #define POISON_SIZE        64
      
      uint32_t
      poison_value(void *v)
 1167 {
              ulong l = (u_long)v;
      
              l = l >> PAGE_SHIFT;
      
              switch (l & 3) {
              case 0:
                      return POISON0;
              case 1:
                      return POISON1;
              case 2:
                      return (POISON0 & 0xffff0000) | (~POISON0 & 0x0000ffff);
              case 3:
                      return (POISON1 & 0xffff0000) | (~POISON1 & 0x0000ffff);
              }
              return 0;
      }
      
      void
      poison_mem(void *v, size_t len)
 3936 {
              uint32_t *ip = v;
              size_t i;
              uint32_t poison;
      
              poison = poison_value(v);
      
              if (len > POISON_SIZE)
                      len = POISON_SIZE;
              len = len / sizeof(*ip);
 3936         for (i = 0; i < len; i++)
 1714                 ip[i] = poison;
      }
      
      int
      poison_check(void *v, size_t len, size_t *pidx, uint32_t *pval)
 5712 {
              uint32_t *ip = v;
              size_t i;
              uint32_t poison;
      
              poison = poison_value(v);
      
              if (len > POISON_SIZE)
                      len = POISON_SIZE;
              len = len / sizeof(*ip);
 5712         for (i = 0; i < len; i++) {
                      if (ip[i] != poison) {
                              *pidx = i;
                              *pval = poison;
                              return 1;
                      }
              }
              return 0;
      }
      
      /*        $OpenBSD: if.c,v 1.587 2019/08/06 22:57:54 bluhm Exp $        */
      /*        $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej 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) 1980, 1986, 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.
       *
       *        @(#)if.c        8.3 (Berkeley) 1/4/94
       */
      
      #include "bpfilter.h"
      #include "bridge.h"
      #include "carp.h"
      #include "ether.h"
      #include "pf.h"
      #include "pfsync.h"
      #include "ppp.h"
      #include "pppoe.h"
      #include "switch.h"
      #include "trunk.h"
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/mbuf.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/timeout.h>
      #include <sys/protosw.h>
      #include <sys/kernel.h>
      #include <sys/ioctl.h>
      #include <sys/domain.h>
      #include <sys/task.h>
      #include <sys/atomic.h>
      #include <sys/percpu.h>
      #include <sys/proc.h>
      
      #include <dev/rndvar.h>
      
      #include <net/if.h>
      #include <net/if_dl.h>
      #include <net/if_types.h>
      #include <net/route.h>
      #include <net/netisr.h>
      
      #include <netinet/in.h>
      #include <netinet/if_ether.h>
      #include <netinet/igmp.h>
      #ifdef MROUTING
      #include <netinet/ip_mroute.h>
      #endif
      
      #ifdef INET6
      #include <netinet6/in6_var.h>
      #include <netinet6/in6_ifattach.h>
      #include <netinet6/nd6.h>
      #include <netinet/ip6.h>
      #include <netinet6/ip6_var.h>
      #endif
      
      #ifdef MPLS
      #include <netmpls/mpls.h>
      #endif
      
      #if NBPFILTER > 0
      #include <net/bpf.h>
      #endif
      
      #if NBRIDGE > 0
      #include <net/if_bridge.h>
      #endif
      
      #if NCARP > 0
      #include <netinet/ip_carp.h>
      #endif
      
      #if NPF > 0
      #include <net/pfvar.h>
      #endif
      
      #include <sys/device.h>
      
      void        if_attachsetup(struct ifnet *);
      void        if_attachdomain(struct ifnet *);
      void        if_attach_common(struct ifnet *);
      int        if_createrdomain(int, struct ifnet *);
      int        if_setrdomain(struct ifnet *, int);
      void        if_slowtimo(void *);
      
      void        if_detached_qstart(struct ifqueue *);
      int        if_detached_ioctl(struct ifnet *, u_long, caddr_t);
      
      int        ifioctl_get(u_long, caddr_t);
      int        ifconf(caddr_t);
      static int
              if_sffpage_check(const caddr_t);
      
      int        if_getgroup(caddr_t, struct ifnet *);
      int        if_getgroupmembers(caddr_t);
      int        if_getgroupattribs(caddr_t);
      int        if_setgroupattribs(caddr_t);
      int        if_getgrouplist(caddr_t);
      
      void        if_linkstate(struct ifnet *);
      void        if_linkstate_task(void *);
      
      int        if_clone_list(struct if_clonereq *);
      struct if_clone        *if_clone_lookup(const char *, int *);
      
      int        if_group_egress_build(void);
      
      void        if_watchdog_task(void *);
      
      void        if_netisr(void *);
      
      #ifdef DDB
      void        ifa_print_all(void);
      #endif
      
      void        if_qstart_compat(struct ifqueue *);
      
      /*
       * interface index map
       *
       * the kernel maintains a mapping of interface indexes to struct ifnet
       * pointers.
       *
       * the map is an array of struct ifnet pointers prefixed by an if_map
       * structure. the if_map structure stores the length of its array.
       *
       * as interfaces are attached to the system, the map is grown on demand
       * up to USHRT_MAX entries.
       *
       * interface index 0 is reserved and represents no interface. this
       * supports the use of the interface index as the scope for IPv6 link
       * local addresses, where scope 0 means no scope has been specified.
       * it also supports the use of interface index as the unique identifier
       * for network interfaces in SNMP applications as per RFC2863. therefore
       * if_get(0) returns NULL.
       */
      
      void if_ifp_dtor(void *, void *);
      void if_map_dtor(void *, void *);
      struct ifnet *if_ref(struct ifnet *);
      
      /*
       * struct if_map
       *
       * bounded array of ifnet srp pointers used to fetch references of live
       * interfaces with if_get().
       */
      
      struct if_map {
              unsigned long                 limit;
              /* followed by limit ifnet srp pointers */
      };
      
      /*
       * struct if_idxmap
       *
       * infrastructure to manage updates and accesses to the current if_map.
       */
      
      struct if_idxmap {
              unsigned int                 serial;
              unsigned int                 count;
              struct srp                 map;
      };
      
      void        if_idxmap_init(unsigned int);
      void        if_idxmap_insert(struct ifnet *);
      void        if_idxmap_remove(struct ifnet *);
      
      TAILQ_HEAD(, ifg_group) ifg_head = TAILQ_HEAD_INITIALIZER(ifg_head);
      
      LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
      int if_cloners_count;
      
      struct timeout net_tick_to;
      void        net_tick(void *);
      int        net_livelocked(void);
      int        ifq_congestion;
      
      int                 netisr;
      
      #define        NET_TASKQ        1
      struct taskq        *nettqmp[NET_TASKQ];
      
      struct task if_input_task_locked = TASK_INITIALIZER(if_netisr, NULL);
      
      /*
       * Serialize socket operations to ensure no new sleeping points
       * are introduced in IP output paths.
       */
      struct rwlock netlock = RWLOCK_INITIALIZER("netlock");
      
      /*
       * Network interface utility routines.
       */
      void
      ifinit(void)
      {
              unsigned int        i;
      
              /*
               * most machines boot with 4 or 5 interfaces, so size the initial map
               * to accomodate this
               */
              if_idxmap_init(8);
      
              timeout_set(&net_tick_to, net_tick, &net_tick_to);
      
              for (i = 0; i < NET_TASKQ; i++) {
                      nettqmp[i] = taskq_create("softnet", 1, IPL_NET, TASKQ_MPSAFE);
                      if (nettqmp[i] == NULL)
                              panic("unable to create network taskq %d", i);
              }
      
              net_tick(&net_tick_to);
      }
      
      static struct if_idxmap if_idxmap = {
              0,
              0,
              SRP_INITIALIZER()
      };
      
      struct srp_gc if_ifp_gc = SRP_GC_INITIALIZER(if_ifp_dtor, NULL);
      struct srp_gc if_map_gc = SRP_GC_INITIALIZER(if_map_dtor, NULL);
      
      struct ifnet_head ifnet = TAILQ_HEAD_INITIALIZER(ifnet);
      
      void
      if_idxmap_init(unsigned int limit)
      {
              struct if_map *if_map;
              struct srp *map;
              unsigned int i;
      
              if_idxmap.serial = 1; /* skip ifidx 0 so it can return NULL */
      
              if_map = malloc(sizeof(*if_map) + limit * sizeof(*map),
                  M_IFADDR, M_WAITOK);
      
              if_map->limit = limit;
              map = (struct srp *)(if_map + 1);
              for (i = 0; i < limit; i++)
                      srp_init(&map[i]);
      
              /* this is called early so there's nothing to race with */
              srp_update_locked(&if_map_gc, &if_idxmap.map, if_map);
      }
      
      void
      if_idxmap_insert(struct ifnet *ifp)
   27 {
              struct if_map *if_map;
              struct srp *map;
              unsigned int index, i;
      
              refcnt_init(&ifp->if_refcnt);
      
              /* the kernel lock guarantees serialised modifications to if_idxmap */
              KERNEL_ASSERT_LOCKED();
      
              if (++if_idxmap.count > USHRT_MAX)
                      panic("too many interfaces");
      
              if_map = srp_get_locked(&if_idxmap.map);
              map = (struct srp *)(if_map + 1);
      
              index = if_idxmap.serial++ & USHRT_MAX;
      
   27         if (index >= if_map->limit) {
                      struct if_map *nif_map;
                      struct srp *nmap;
                      unsigned int nlimit;
    5                 struct ifnet *nifp;
      
                      nlimit = if_map->limit * 2;
                      nif_map = malloc(sizeof(*nif_map) + nlimit * sizeof(*nmap),
                          M_IFADDR, M_WAITOK);
                      nmap = (struct srp *)(nif_map + 1);
      
                      nif_map->limit = nlimit;
    5                 for (i = 0; i < if_map->limit; i++) {
                              srp_init(&nmap[i]);
                              nifp = srp_get_locked(&map[i]);
    5                         if (nifp != NULL) {
                                      srp_update_locked(&if_ifp_gc, &nmap[i],
                                          if_ref(nifp));
                              }
                      }
      
    5                 while (i < nlimit) {
                              srp_init(&nmap[i]);
                              i++;
                      }
      
                      srp_update_locked(&if_map_gc, &if_idxmap.map, nif_map);
                      if_map = nif_map;
                      map = nmap;
              }
      
              /* pick the next free index */
              for (i = 0; i < USHRT_MAX; i++) {
   27                 if (index != 0 && srp_get_locked(&map[index]) == NULL)
                              break;
      
                      index = if_idxmap.serial++ & USHRT_MAX;
              }
      
              /* commit */
              ifp->if_index = index;
              srp_update_locked(&if_ifp_gc, &map[index], if_ref(ifp));
      }
      
      void
      if_idxmap_remove(struct ifnet *ifp)
   23 {
              struct if_map *if_map;
              struct srp *map;
              unsigned int index;
      
              index = ifp->if_index;
      
              /* the kernel lock guarantees serialised modifications to if_idxmap */
              KERNEL_ASSERT_LOCKED();
      
              if_map = srp_get_locked(&if_idxmap.map);
              KASSERT(index < if_map->limit);
      
              map = (struct srp *)(if_map + 1);
              KASSERT(ifp == (struct ifnet *)srp_get_locked(&map[index]));
      
   23         srp_update_locked(&if_ifp_gc, &map[index], NULL);
              if_idxmap.count--;
              /* end of if_idxmap modifications */
      
              /* sleep until the last reference is released */
              refcnt_finalize(&ifp->if_refcnt, "ifidxrm");
      }
      
      void
      if_ifp_dtor(void *null, void *ifp)
   26 {
   26         if_put(ifp);
      }
      
      void
      if_map_dtor(void *null, void *m)
    5 {
              struct if_map *if_map = m;
              struct srp *map = (struct srp *)(if_map + 1);
              unsigned int i;
      
              /*
               * dont need to serialize the use of update_locked since this is
               * the last reference to this map. there's nothing to race against.
               */
    5         for (i = 0; i < if_map->limit; i++)
                      srp_update_locked(&if_ifp_gc, &map[i], NULL);
      
    5         free(if_map, M_IFADDR, sizeof(*if_map) + if_map->limit * sizeof(*map));
      }
      
      /*
       * Attach an interface to the
       * list of "active" interfaces.
       */
      void
      if_attachsetup(struct ifnet *ifp)
   27 {
              unsigned long ifidx;
      
   27         NET_ASSERT_LOCKED();
      
              TAILQ_INIT(&ifp->if_groups);
      
              if_addgroup(ifp, IFG_ALL);
      
   27         if_attachdomain(ifp);
      #if NPF > 0
              pfi_attach_ifnet(ifp);
      #endif
      
              timeout_set(&ifp->if_slowtimo, if_slowtimo, ifp);
   27         if_slowtimo(ifp);
      
              if_idxmap_insert(ifp);
   27         KASSERT(if_get(0) == NULL);
      
   27         ifidx = ifp->if_index;
      
              task_set(&ifp->if_watchdogtask, if_watchdog_task, (void *)ifidx);
              task_set(&ifp->if_linkstatetask, if_linkstate_task, (void *)ifidx);
      
              /* Announce the interface. */
              rtm_ifannounce(ifp, IFAN_ARRIVAL);
      }
      
      /*
       * Allocate the link level name for the specified interface.  This
       * is an attachment helper.  It must be called after ifp->if_addrlen
       * is initialized, which may not be the case when if_attach() is
       * called.
       */
      void
      if_alloc_sadl(struct ifnet *ifp)
   27 {
              unsigned int socksize;
              int namelen, masklen;
              struct sockaddr_dl *sdl;
      
              /*
               * If the interface already has a link name, release it
               * now.  This is useful for interfaces that can change
               * link types, and thus switch link names often.
               */
   27         if_free_sadl(ifp);
      
              namelen = strlen(ifp->if_xname);
              masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
              socksize = masklen + ifp->if_addrlen;
      #define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
              if (socksize < sizeof(*sdl))
                      socksize = sizeof(*sdl);
              socksize = ROUNDUP(socksize);
              sdl = malloc(socksize, M_IFADDR, M_WAITOK|M_ZERO);
              sdl->sdl_len = socksize;
              sdl->sdl_family = AF_LINK;
              bcopy(ifp->if_xname, sdl->sdl_data, namelen);
              sdl->sdl_nlen = namelen;
              sdl->sdl_alen = ifp->if_addrlen;
              sdl->sdl_index = ifp->if_index;
              sdl->sdl_type = ifp->if_type;
              ifp->if_sadl = sdl;
      }
      
      /*
       * Free the link level name for the specified interface.  This is
       * a detach helper.  This is called from if_detach() or from
       * link layer type specific detach functions.
       */
      void
      if_free_sadl(struct ifnet *ifp)
      {
   27         if (ifp->if_sadl == NULL)
                      return;
      
   20         free(ifp->if_sadl, M_IFADDR, ifp->if_sadl->sdl_len);
              ifp->if_sadl = NULL;
      }
      
      void
      if_attachdomain(struct ifnet *ifp)
      {
              struct domain *dp;
              int i, s;
      
              s = splnet();
      
              /* address family dependent data region */
              bzero(ifp->if_afdata, sizeof(ifp->if_afdata));
   27         for (i = 0; (dp = domains[i]) != NULL; i++) {
   27                 if (dp->dom_ifattach)
                              ifp->if_afdata[dp->dom_family] =
   27                             (*dp->dom_ifattach)(ifp);
              }
      
              splx(s);
      }
      
      void
      if_attachhead(struct ifnet *ifp)
      {
              if_attach_common(ifp);
              NET_LOCK();
              TAILQ_INSERT_HEAD(&ifnet, ifp, if_list);
              if_attachsetup(ifp);
              NET_UNLOCK();
      }
      
      void
      if_attach(struct ifnet *ifp)
   27 {
              if_attach_common(ifp);
              NET_LOCK();
              TAILQ_INSERT_TAIL(&ifnet, ifp, if_list);
              if_attachsetup(ifp);
              NET_UNLOCK();
      }
      
      void
      if_attach_queues(struct ifnet *ifp, unsigned int nqs)
      {
              struct ifqueue **map;
              struct ifqueue *ifq;
              int i;
      
              KASSERT(ifp->if_ifqs == ifp->if_snd.ifq_ifqs);
              KASSERT(nqs != 0);
      
              map = mallocarray(sizeof(*map), nqs, M_DEVBUF, M_WAITOK);
      
              ifp->if_snd.ifq_softc = NULL;
              map[0] = &ifp->if_snd;
      
              for (i = 1; i < nqs; i++) {
                      ifq = malloc(sizeof(*ifq), M_DEVBUF, M_WAITOK|M_ZERO);
                      ifq_set_maxlen(ifq, ifp->if_snd.ifq_maxlen);
                      ifq_init(ifq, ifp, i);
                      map[i] = ifq;
              }
      
              ifp->if_ifqs = map;
              ifp->if_nifqs = nqs;
      }
      
      void
      if_attach_iqueues(struct ifnet *ifp, unsigned int niqs)
      {
              struct ifiqueue **map;
              struct ifiqueue *ifiq;
              unsigned int i;
      
              KASSERT(niqs != 0);
      
              map = mallocarray(niqs, sizeof(*map), M_DEVBUF, M_WAITOK);
      
              ifp->if_rcv.ifiq_softc = NULL;
              map[0] = &ifp->if_rcv;
      
              for (i = 1; i < niqs; i++) {
                      ifiq = malloc(sizeof(*ifiq), M_DEVBUF, M_WAITOK|M_ZERO);
                      ifiq_init(ifiq, ifp, i);
                      map[i] = ifiq;
              }
      
              ifp->if_iqs = map;
              ifp->if_niqs = niqs;
      }
      
      void
      if_attach_common(struct ifnet *ifp)
   27 {
              KASSERT(ifp->if_ioctl != NULL);
      
              TAILQ_INIT(&ifp->if_addrlist);
              TAILQ_INIT(&ifp->if_maddrlist);
      
              if (!ISSET(ifp->if_xflags, IFXF_MPSAFE)) {
                      KASSERTMSG(ifp->if_qstart == NULL,
                          "%s: if_qstart set without MPSAFE set", ifp->if_xname);
   27                 ifp->if_qstart = if_qstart_compat;
              } else {
                      KASSERTMSG(ifp->if_start == NULL,
                          "%s: if_start set with MPSAFE set", ifp->if_xname);
                      KASSERTMSG(ifp->if_qstart != NULL,
                          "%s: if_qstart not set with MPSAFE set", ifp->if_xname);
              }
      
              ifq_init(&ifp->if_snd, ifp, 0);
      
              ifp->if_snd.ifq_ifqs[0] = &ifp->if_snd;
              ifp->if_ifqs = ifp->if_snd.ifq_ifqs;
              ifp->if_nifqs = 1;
              if (ifp->if_txmit == 0)
   27                 ifp->if_txmit = IF_TXMIT_DEFAULT;
      
              ifiq_init(&ifp->if_rcv, ifp, 0);
      
              ifp->if_rcv.ifiq_ifiqs[0] = &ifp->if_rcv;
              ifp->if_iqs = ifp->if_rcv.ifiq_ifiqs;
              ifp->if_niqs = 1;
      
              ifp->if_addrhooks = malloc(sizeof(*ifp->if_addrhooks),
                  M_TEMP, M_WAITOK);
              TAILQ_INIT(ifp->if_addrhooks);
              ifp->if_linkstatehooks = malloc(sizeof(*ifp->if_linkstatehooks),
                  M_TEMP, M_WAITOK);
              TAILQ_INIT(ifp->if_linkstatehooks);
              ifp->if_detachhooks = malloc(sizeof(*ifp->if_detachhooks),
                  M_TEMP, M_WAITOK);
              TAILQ_INIT(ifp->if_detachhooks);
      
   21         if (ifp->if_rtrequest == NULL)
    6                 ifp->if_rtrequest = if_rtrequest_dummy;
              if (ifp->if_enqueue == NULL)
   27                 ifp->if_enqueue = if_enqueue_ifq;
              ifp->if_llprio = IFQ_DEFPRIO;
      
              SRPL_INIT(&ifp->if_inputs);
      }
      
      void
      if_attach_ifq(struct ifnet *ifp, const struct ifq_ops *newops, void *args)
      {
              /*
               * only switch the ifq_ops on the first ifq on an interface.
               *
               * the only ifq_ops we provide priq and hfsc, and hfsc only
               * works on a single ifq. because the code uses the ifq_ops
               * on the first ifq (if_snd) to select a queue for an mbuf,
               * by switching only the first one we change both the algorithm
               * and force the routing of all new packets to it.
               */
              ifq_attach(&ifp->if_snd, newops, args);
      }
      
      void
      if_start(struct ifnet *ifp)
      {
              KASSERT(ifp->if_qstart == if_qstart_compat);
              if_qstart_compat(&ifp->if_snd);
      }
      void
      if_qstart_compat(struct ifqueue *ifq)
   53 {
              struct ifnet *ifp = ifq->ifq_if;
              int s;
      
              /*
               * the stack assumes that an interface can have multiple
               * transmit rings, but a lot of drivers are still written
               * so that interfaces and send rings have a 1:1 mapping.
               * this provides compatability between the stack and the older
               * drivers by translating from the only queue they have
               * (ifp->if_snd) back to the interface and calling if_start.
                */
      
              KERNEL_LOCK();
              s = splnet();
              (*ifp->if_start)(ifp);
              splx(s);
              KERNEL_UNLOCK();
      }
      
      int
      if_enqueue(struct ifnet *ifp, struct mbuf *m)
  352 {
      #if NPF > 0
              if (m->m_pkthdr.pf.delay > 0)
                      return (pf_delay_pkt(m, ifp->if_index));
      #endif
      
      #if NBRIDGE > 0
  352         if (ifp->if_bridgeidx && (m->m_flags & M_PROTO1) == 0) {
                      int error;
      
                      error = bridge_enqueue(ifp, m);
                      return (error);
              }
      #endif
      
      #if NPF > 0
              pf_pkt_addr_changed(m);
      #endif        /* NPF > 0 */
      
              return ((*ifp->if_enqueue)(ifp, m));
      }
      
      int
      if_enqueue_ifq(struct ifnet *ifp, struct mbuf *m)
  352 {
              struct ifqueue *ifq = &ifp->if_snd;
              int error;
      
  352         if (ifp->if_nifqs > 1) {
                      unsigned int idx;
      
                      /*
                       * use the operations on the first ifq to pick which of
                       * the array gets this mbuf.
                       */
      
                      idx = ifq_idx(&ifp->if_snd, ifp->if_nifqs, m);
                      ifq = ifp->if_ifqs[idx];
              }
      
              error = ifq_enqueue(ifq, m);
              if (error)
                      return (error);
      
  352         ifq_start(ifq);
      
              return (0);
      }
      
      void
      if_input(struct ifnet *ifp, struct mbuf_list *ml)
 1656 {
              ifiq_input(&ifp->if_rcv, ml);
      }
      
      int
      if_input_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
  111 {
      #if NBPFILTER > 0
              /*
               * Only send packets to bpf if they are destinated to local
               * addresses.
               *
               * if_input_local() is also called for SIMPLEX interfaces to
               * duplicate packets for local use.  But don't dup them to bpf.
               */
   84         if (ifp->if_flags & IFF_LOOPBACK) {
                      caddr_t if_bpf = ifp->if_bpf;
      
   27                 if (if_bpf)
                              bpf_mtap_af(if_bpf, af, m, BPF_DIRECTION_OUT);
              }
      #endif
              m_resethdr(m);
              m->m_flags |= M_LOOP;
              m->m_pkthdr.ph_ifidx = ifp->if_index;
              m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
      
              ifp->if_opackets++;
              ifp->if_obytes += m->m_pkthdr.len;
      
              ifp->if_ipackets++;
              ifp->if_ibytes += m->m_pkthdr.len;
      
              switch (af) {
              case AF_INET:
   99                 ipv4_input(ifp, m);
                      break;
      #ifdef INET6
              case AF_INET6:
   12                 ipv6_input(ifp, m);
                      break;
      #endif /* INET6 */
      #ifdef MPLS
              case AF_MPLS:
                      mpls_input(ifp, m);
                      break;
      #endif /* MPLS */
              default:
                      printf("%s: can't handle af%d\n", ifp->if_xname, af);
                      m_freem(m);
                      return (EAFNOSUPPORT);
              }
      
              return (0);
      }
      
      int
      if_output_local(struct ifnet *ifp, struct mbuf *m, sa_family_t af)
   51 {
              struct ifiqueue *ifiq;
              unsigned int flow = 0;
      
              m->m_pkthdr.ph_family = af;
              m->m_pkthdr.ph_ifidx = ifp->if_index;
              m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
      
              if (ISSET(m->m_pkthdr.ph_flowid, M_FLOWID_VALID))
                      flow = m->m_pkthdr.ph_flowid & M_FLOWID_MASK;
      
              ifiq = ifp->if_iqs[flow % ifp->if_niqs];
      
              return (ifiq_enqueue(ifiq, m) == 0 ? 0 : ENOBUFS);
      }
      
      struct ifih {
              SRPL_ENTRY(ifih)          ifih_next;
              int                        (*ifih_input)(struct ifnet *, struct mbuf *,
                                            void *);
              void                         *ifih_cookie;
              int                          ifih_refcnt;
              struct refcnt                  ifih_srpcnt;
      };
      
      void        if_ih_ref(void *, void *);
      void        if_ih_unref(void *, void *);
      
      struct srpl_rc ifih_rc = SRPL_RC_INITIALIZER(if_ih_ref, if_ih_unref, NULL);
      
      void
      if_ih_insert(struct ifnet *ifp, int (*input)(struct ifnet *, struct mbuf *,
          void *), void *cookie)
    6 {
              struct ifih *ifih;
      
              /* the kernel lock guarantees serialised modifications to if_inputs */
              KERNEL_ASSERT_LOCKED();
      
    6         SRPL_FOREACH_LOCKED(ifih, &ifp->if_inputs, ifih_next) {
                      if (ifih->ifih_input == input && ifih->ifih_cookie == cookie) {
                              ifih->ifih_refcnt++;
                              break;
                      }
              }
      
              if (ifih == NULL) {
                      ifih = malloc(sizeof(*ifih), M_DEVBUF, M_WAITOK);
      
                      ifih->ifih_input = input;
                      ifih->ifih_cookie = cookie;
                      ifih->ifih_refcnt = 1;
                      refcnt_init(&ifih->ifih_srpcnt);
    6                 SRPL_INSERT_HEAD_LOCKED(&ifih_rc, &ifp->if_inputs,
                          ifih, ifih_next);
              }
      }
      
      void
      if_ih_ref(void *null, void *i)
    6 {
              struct ifih *ifih = i;
      
              refcnt_take(&ifih->ifih_srpcnt);
      }
      
      void
      if_ih_unref(void *null, void *i)
    9 {
              struct ifih *ifih = i;
      
              refcnt_rele_wake(&ifih->ifih_srpcnt);
      }
      
      void
      if_ih_remove(struct ifnet *ifp, int (*input)(struct ifnet *, struct mbuf *,
          void *), void *cookie)
    9 {
              struct ifih *ifih;
      
              /* the kernel lock guarantees serialised modifications to if_inputs */
              KERNEL_ASSERT_LOCKED();
      
              SRPL_FOREACH_LOCKED(ifih, &ifp->if_inputs, ifih_next) {
                      if (ifih->ifih_input == input && ifih->ifih_cookie == cookie)
                              break;
              }
      
              KASSERT(ifih != NULL);
      
              if (--ifih->ifih_refcnt == 0) {
    9                 SRPL_REMOVE_LOCKED(&ifih_rc, &ifp->if_inputs, ifih,
                          ifih, ifih_next);
      
                      refcnt_finalize(&ifih->ifih_srpcnt, "ifihrm");
                      free(ifih, M_DEVBUF, sizeof(*ifih));
              }
      }
      
      static void
      if_ih_input(struct ifnet *ifp, struct mbuf *m)
      {
              struct ifih *ifih;
              struct srp_ref sr;
      
              /*
               * Pass this mbuf to all input handlers of its
               * interface until it is consumed.
               */
              SRPL_FOREACH(ifih, &sr, &ifp->if_inputs, ifih_next) {
                      if ((*ifih->ifih_input)(ifp, m, ifih->ifih_cookie))
                              break;
              }
              SRPL_LEAVE(&sr);
      
              if (ifih == NULL)
                      m_freem(m);
      }
      
      void
      if_input_process(struct ifnet *ifp, struct mbuf_list *ml)
      {
              struct mbuf *m;
      
              if (ml_empty(ml))
                      return;
      
              if (!ISSET(ifp->if_xflags, IFXF_CLONED))
                      enqueue_randomness(ml_len(ml));
      
              /*
               * We grab the NET_LOCK() before processing any packet to
               * ensure there's no contention on the routing table lock.
               *
               * Without it we could race with a userland thread to insert
               * a L2 entry in ip{6,}_output().  Such race would result in
               * one of the threads sleeping *inside* the IP output path.
               *
               * Since we have a NET_LOCK() we also use it to serialize access
               * to PF globals, pipex globals, unicast and multicast addresses
               * lists.
               */
              NET_RLOCK();
              while ((m = ml_dequeue(ml)) != NULL)
                      if_ih_input(ifp, m);
              NET_RUNLOCK();
      }
      
      void
      if_vinput(struct ifnet *ifp, struct mbuf *m)
      {
      #if NBPFILTER > 0
              caddr_t if_bpf;
      #endif
      
              m->m_pkthdr.ph_ifidx = ifp->if_index;
              m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
      
              counters_pkt(ifp->if_counters,
                  ifc_ipackets, ifc_ibytes, m->m_pkthdr.len);
      
      #if NBPFILTER > 0
              if_bpf = ifp->if_bpf;
              if (if_bpf) {
                      if (bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_IN)) {
                              m_freem(m);
                              return;
                      }
              }
      #endif
      
              if_ih_input(ifp, m);
      }
      
      void
      if_netisr(void *unused)
      {
              int n, t = 0;
      
              NET_RLOCK();
      
              while ((n = netisr) != 0) {
                      /* Like sched_pause() but with a rwlock dance. */
                      if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD) {
                              NET_RUNLOCK();
                              yield();
                              NET_RLOCK();
                      }
      
                      atomic_clearbits_int(&netisr, n);
      
      #if NETHER > 0
                      if (n & (1 << NETISR_ARP)) {
                              KERNEL_LOCK();
                              arpintr();
                              KERNEL_UNLOCK();
                      }
      #endif
      #if NPPP > 0
                      if (n & (1 << NETISR_PPP)) {
                              KERNEL_LOCK();
                              pppintr();
                              KERNEL_UNLOCK();
                      }
      #endif
      #if NBRIDGE > 0
                      if (n & (1 << NETISR_BRIDGE))
                              bridgeintr();
      #endif
      #if NSWITCH > 0
                      if (n & (1 << NETISR_SWITCH)) {
                              KERNEL_LOCK();
                              switchintr();
                              KERNEL_UNLOCK();
                      }
      #endif
      #if NPPPOE > 0
                      if (n & (1 << NETISR_PPPOE)) {
                              KERNEL_LOCK();
                              pppoeintr();
                              KERNEL_UNLOCK();
                      }
      #endif
      #ifdef PIPEX
                      if (n & (1 << NETISR_PIPEX)) {
                              KERNEL_LOCK();
                              pipexintr();
                              KERNEL_UNLOCK();
                      }
      #endif
                      t |= n;
              }
      
      #if NPFSYNC > 0
              if (t & (1 << NETISR_PFSYNC)) {
                      KERNEL_LOCK();
                      pfsyncintr();
                      KERNEL_UNLOCK();
              }
      #endif
      
              NET_RUNLOCK();
      }
      
      void
      if_deactivate(struct ifnet *ifp)
    9 {
              NET_LOCK();
              /*
               * Call detach hooks from head to tail.  To make sure detach
               * hooks are executed in the reverse order they were added, all
               * the hooks have to be added to the head!
               */
              dohooks(ifp->if_detachhooks, HOOK_REMOVE | HOOK_FREE);
      
              NET_UNLOCK();
      }
      
      /*
       * Detach an interface from everything in the kernel.  Also deallocate
       * private resources.
       */
      void
      if_detach(struct ifnet *ifp)
   23 {
              struct ifaddr *ifa;
              struct ifg_list *ifg;
              struct domain *dp;
              int i, s;
      
              /* Undo pseudo-driver changes. */
              if_deactivate(ifp);
      
              ifq_clr_oactive(&ifp->if_snd);
      
              /* Other CPUs must not have a reference before we start destroying. */
              if_idxmap_remove(ifp);
      
      #if NBPFILTER > 0
              bpfdetach(ifp);
      #endif
      
              NET_LOCK();
              s = splnet();
              ifp->if_qstart = if_detached_qstart;
              ifp->if_ioctl = if_detached_ioctl;
              ifp->if_watchdog = NULL;
      
              /* Remove the watchdog timeout & task */
              timeout_del(&ifp->if_slowtimo);
              task_del(net_tq(ifp->if_index), &ifp->if_watchdogtask);
      
              /* Remove the link state task */
              task_del(net_tq(ifp->if_index), &ifp->if_linkstatetask);
      
              rti_delete(ifp);
      #if NETHER > 0 && defined(NFSCLIENT)
   23         if (ifp->if_index == revarp_ifidx)
                      revarp_ifidx = 0;
      #endif
      #ifdef MROUTING
              vif_delete(ifp);
      #endif
              in_ifdetach(ifp);
      #ifdef INET6
              in6_ifdetach(ifp);
      #endif
      #if NPF > 0
              pfi_detach_ifnet(ifp);
      #endif
      
              /* Remove the interface from the list of all interfaces.  */
   20         TAILQ_REMOVE(&ifnet, ifp, if_list);
      
   20         while ((ifg = TAILQ_FIRST(&ifp->if_groups)) != NULL)
                      if_delgroup(ifp, ifg->ifgl_group->ifg_group);
      
   20         if_free_sadl(ifp);
      
              /* We should not have any address left at this point. */
   20         if (!TAILQ_EMPTY(&ifp->if_addrlist)) {
      #ifdef DIAGNOSTIC
                      printf("%s: address list non empty\n", ifp->if_xname);
      #endif
                      while ((ifa = TAILQ_FIRST(&ifp->if_addrlist)) != NULL) {
                              ifa_del(ifp, ifa);
                              ifa->ifa_ifp = NULL;
                              ifafree(ifa);
                      }
              }
      
              free(ifp->if_addrhooks, M_TEMP, sizeof(*ifp->if_addrhooks));
              free(ifp->if_linkstatehooks, M_TEMP, sizeof(*ifp->if_linkstatehooks));
              free(ifp->if_detachhooks, M_TEMP, sizeof(*ifp->if_detachhooks));
      
   20         for (i = 0; (dp = domains[i]) != NULL; i++) {
   20                 if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family])
   20                         (*dp->dom_ifdetach)(ifp,
                                  ifp->if_afdata[dp->dom_family]);
              }
      
              /* Announce that the interface is gone. */
              rtm_ifannounce(ifp, IFAN_DEPARTURE);
              splx(s);
              NET_UNLOCK();
      
   20         if (ifp->if_counters != NULL)
                      if_counters_free(ifp);
      
   20         for (i = 0; i < ifp->if_nifqs; i++)
                      ifq_destroy(ifp->if_ifqs[i]);
   20         if (ifp->if_ifqs != ifp->if_snd.ifq_ifqs) {
                      for (i = 1; i < ifp->if_nifqs; i++) {
                              free(ifp->if_ifqs[i], M_DEVBUF,
                                  sizeof(struct ifqueue));
                      }
                      free(ifp->if_ifqs, M_DEVBUF,
                          sizeof(struct ifqueue *) * ifp->if_nifqs);
              }
      
   20         for (i = 0; i < ifp->if_niqs; i++)
                      ifiq_destroy(ifp->if_iqs[i]);
   20         if (ifp->if_iqs != ifp->if_rcv.ifiq_ifiqs) {
                      for (i = 1; i < ifp->if_niqs; i++) {
                              free(ifp->if_iqs[i], M_DEVBUF,
                                  sizeof(struct ifiqueue));
                      }
                      free(ifp->if_iqs, M_DEVBUF,
                          sizeof(struct ifiqueue *) * ifp->if_niqs);
              }
      }
      
      /*
       * Returns true if ``ifp0'' is connected to the interface with index ``ifidx''.
       */
      int
      if_isconnected(const struct ifnet *ifp0, unsigned int ifidx)
      {
              struct ifnet *ifp;
              int connected = 0;
      
              ifp = if_get(ifidx);
              if (ifp == NULL)
                      return (0);
      
              if (ifp0->if_index == ifp->if_index)
                      connected = 1;
      
      #if NBRIDGE > 0
              if (ifp0->if_bridgeidx != 0 && ifp0->if_bridgeidx == ifp->if_bridgeidx)
                      connected = 1;
      #endif
      #if NCARP > 0
              if ((ifp0->if_type == IFT_CARP && ifp0->if_carpdev == ifp) ||
                  (ifp->if_type == IFT_CARP && ifp->if_carpdev == ifp0))
                      connected = 1;
      #endif
      
              if_put(ifp);
              return (connected);
      }
      
      /*
       * Create a clone network interface.
       */
      int
      if_clone_create(const char *name, int rdomain)
   35 {
              struct if_clone *ifc;
              struct ifnet *ifp;
              int unit, ret;
      
              ifc = if_clone_lookup(name, &unit);
    6         if (ifc == NULL)
                      return (EINVAL);
      
   29         if (ifunit(name) != NULL)
                      return (EEXIST);
      
              ret = (*ifc->ifc_create)(ifc, unit);
      
   27         if (ret != 0 || (ifp = ifunit(name)) == NULL)
                      return (ret);
      
              NET_LOCK();
              if_addgroup(ifp, ifc->ifc_name);
   27         if (rdomain != 0)
                      if_setrdomain(ifp, rdomain);
              NET_UNLOCK();
      
              return (ret);
      }
      
      /*
       * Destroy a clone network interface.
       */
      int
      if_clone_destroy(const char *name)
   31 {
              struct if_clone *ifc;
              struct ifnet *ifp;
              int ret;
      
              ifc = if_clone_lookup(name, NULL);
    5         if (ifc == NULL)
                      return (EINVAL);
      
   26         ifp = ifunit(name);
              if (ifp == NULL)
                      return (ENXIO);
      
              if (ifc->ifc_destroy == NULL)
                      return (EOPNOTSUPP);
      
              NET_LOCK();
   18         if (ifp->if_flags & IFF_UP) {
                      int s;
    5                 s = splnet();
                      if_down(ifp);
                      splx(s);
              }
              NET_UNLOCK();
              ret = (*ifc->ifc_destroy)(ifp);
      
              return (ret);
      }
      
      /*
       * Look up a network interface cloner.
       */
      struct if_clone *
      if_clone_lookup(const char *name, int *unitp)
   59 {
              struct if_clone *ifc;
              const char *cp;
              int unit;
      
              /* separate interface name from unit */
              for (cp = name;
   59             cp - name < IFNAMSIZ && *cp && (*cp < '0' || *cp > '9');
                  cp++)
                      continue;
      
    7         if (cp == name || cp - name == IFNAMSIZ || !*cp)
                      return (NULL);        /* No name or unit number */
      
   52         if (cp - name < IFNAMSIZ-1 && *cp == '0' && cp[1] != '\0')
                      return (NULL);        /* unit number 0 padded */
      
   50         LIST_FOREACH(ifc, &if_cloners, ifc_list) {
   50                 if (strlen(ifc->ifc_name) == cp - name &&
                          !strncmp(name, ifc->ifc_name, cp - name))
                              break;
              }
      
              if (ifc == NULL)
                      return (NULL);
      
              unit = 0;
   48         while (cp - name < IFNAMSIZ && *cp) {
                      if (*cp < '0' || *cp > '9' ||
                          unit > (INT_MAX - (*cp - '0')) / 10) {
                              /* Bogus unit number. */
                              return (NULL);
                      }
                      unit = (unit * 10) + (*cp++ - '0');
              }
      
   26         if (unitp != NULL)
   29                 *unitp = unit;
              return (ifc);
      }
      
      /*
       * Register a network interface cloner.
       */
      void
      if_clone_attach(struct if_clone *ifc)
      {
              /*
               * we are called at kernel boot by main(), when pseudo devices are
               * being attached. The main() is the only guy which may alter the
               * if_cloners. While system is running and main() is done with
               * initialization, the if_cloners becomes immutable. 
               */
              KASSERT(pdevinit_done == 0);
              LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
              if_cloners_count++;
      }
      
      /*
       * Provide list of interface cloners to userspace.
       */
      int
    2 if_clone_list(struct if_clonereq *ifcr)
      {
              char outbuf[IFNAMSIZ], *dst;
              struct if_clone *ifc;
              int count, error = 0;
      
              if ((dst = ifcr->ifcr_buffer) == NULL) {
                      /* Just asking how many there are. */
                      ifcr->ifcr_total = if_cloners_count;
                      return (0);
              }
      
    3         if (ifcr->ifcr_count < 0)
                      return (EINVAL);
      
              ifcr->ifcr_total = if_cloners_count;
              count = MIN(if_cloners_count, ifcr->ifcr_count);
      
    5         LIST_FOREACH(ifc, &if_cloners, ifc_list) {
                      if (count == 0)
                              break;
                      bzero(outbuf, sizeof outbuf);
                      strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
                      error = copyout(outbuf, dst, IFNAMSIZ);
    3                 if (error)
                              break;
                      count--;
                      dst += IFNAMSIZ;
              }
      
              return (error);
      }
      
      /*
       * set queue congestion marker
       */
      void
      if_congestion(void)
      {
              extern int ticks;
      
              ifq_congestion = ticks;
      }
      
      int
      if_congested(void)
   22 {
              extern int ticks;
              int diff;
      
              diff = ticks - ifq_congestion;
              if (diff < 0) {
                      ifq_congestion = ticks - hz;
                      return (0);
              }
      
   22         return (diff <= (hz / 100));
      }
      
      #define        equal(a1, a2)        \
              (bcmp((caddr_t)(a1), (caddr_t)(a2),        \
              (a1)->sa_len) == 0)
      
      /*
       * Locate an interface based on a complete address.
       */
      struct ifaddr *
      ifa_ifwithaddr(struct sockaddr *addr, u_int rtableid)
   61 {
              struct ifnet *ifp;
              struct ifaddr *ifa;
              u_int rdomain;
      
              rdomain = rtable_l2(rtableid);
              KERNEL_LOCK();
   42         TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (ifp->if_rdomain != rdomain)
                              continue;
      
   49                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
   49                         if (ifa->ifa_addr->sa_family != addr->sa_family)
                                      continue;
      
   39                         if (equal(addr, ifa->ifa_addr)) {
   23                                 KERNEL_UNLOCK();
                                      return (ifa);
                              }
                      }
              }
              KERNEL_UNLOCK();
              return (NULL);
      }
      
      /*
       * Locate the point to point interface with a given destination address.
       */
      struct ifaddr *
      ifa_ifwithdstaddr(struct sockaddr *addr, u_int rdomain)
    2 {
              struct ifnet *ifp;
              struct ifaddr *ifa;
      
              rdomain = rtable_l2(rdomain);
              KERNEL_LOCK();
    2         TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (ifp->if_rdomain != rdomain)
                              continue;
    2                 if (ifp->if_flags & IFF_POINTOPOINT) {
    1                         TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                                      if (ifa->ifa_addr->sa_family !=
                                          addr->sa_family || ifa->ifa_dstaddr == NULL)
                                              continue;
                                      if (equal(addr, ifa->ifa_dstaddr)) {
                                              KERNEL_UNLOCK();
                                              return (ifa);
                                      }
                              }
                      }
              }
              KERNEL_UNLOCK();
              return (NULL);
      }
      
      /*
       * Find an interface address specific to an interface best matching
       * a given address.
       */
      struct ifaddr *
      ifaof_ifpforaddr(struct sockaddr *addr, struct ifnet *ifp)
    4 {
              struct ifaddr *ifa;
              char *cp, *cp2, *cp3;
              char *cplim;
              struct ifaddr *ifa_maybe = NULL;
              u_int af = addr->sa_family;
      
              if (af >= AF_MAX)
                      return (NULL);
    4         TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
    3                 if (ifa->ifa_addr->sa_family != af)
                              continue;
                      if (ifa_maybe == NULL)
                              ifa_maybe = ifa;
                      if (ifa->ifa_netmask == 0 || ifp->if_flags & IFF_POINTOPOINT) {
                              if (equal(addr, ifa->ifa_addr) ||
                                  (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
                                      return (ifa);
                              continue;
                      }
                      cp = addr->sa_data;
                      cp2 = ifa->ifa_addr->sa_data;
                      cp3 = ifa->ifa_netmask->sa_data;
                      cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
    1                 for (; cp3 < cplim; cp3++)
    1                         if ((*cp++ ^ *cp2++) & *cp3)
                                      break;
    1                 if (cp3 == cplim)
                              return (ifa);
              }
              return (ifa_maybe);
      }
      
      void
      if_rtrequest_dummy(struct ifnet *ifp, int req, struct rtentry *rt)
      {
      }
      
      /*
       * Default action when installing a local route on a point-to-point
       * interface.
       */
      void
      p2p_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt)
      {
              struct ifnet *lo0ifp;
              struct ifaddr *ifa, *lo0ifa;
      
              switch (req) {
              case RTM_ADD:
                      if (!ISSET(rt->rt_flags, RTF_LOCAL))
                              break;
      
                      TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                              if (memcmp(rt_key(rt), ifa->ifa_addr,
                                  rt_key(rt)->sa_len) == 0)
                                      break;
                      }
      
                      if (ifa == NULL)
                              break;
      
                      KASSERT(ifa == rt->rt_ifa);
      
                      lo0ifp = if_get(rtable_loindex(ifp->if_rdomain));
                      KASSERT(lo0ifp != NULL);
                      TAILQ_FOREACH(lo0ifa, &lo0ifp->if_addrlist, ifa_list) {
                              if (lo0ifa->ifa_addr->sa_family ==
                                  ifa->ifa_addr->sa_family)
                                      break;
                      }
                      if_put(lo0ifp);
      
                      if (lo0ifa == NULL)
                              break;
      
                      rt->rt_flags &= ~RTF_LLINFO;
                      break;
              case RTM_DELETE:
              case RTM_RESOLVE:
              default:
                      break;
              }
      }
      
      
      /*
       * Bring down all interfaces
       */
      void
      if_downall(void)
      {
              struct ifreq ifrq;        /* XXX only partly built */
              struct ifnet *ifp;
      
              NET_LOCK();
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if ((ifp->if_flags & IFF_UP) == 0)
                              continue;
                      if_down(ifp);
                      ifrq.ifr_flags = ifp->if_flags;
                      (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
              }
              NET_UNLOCK();
      }
      
      /*
       * Mark an interface down and notify protocols of
       * the transition.
       */
      void
      if_down(struct ifnet *ifp)
    5 {
    5         NET_ASSERT_LOCKED();
      
              ifp->if_flags &= ~IFF_UP;
              getmicrotime(&ifp->if_lastchange);
              IFQ_PURGE(&ifp->if_snd);
      
    5         if_linkstate(ifp);
      }
      
      /*
       * Mark an interface up and notify protocols of
       * the transition.
       */
      void
      if_up(struct ifnet *ifp)
      {
              NET_ASSERT_LOCKED();
      
              ifp->if_flags |= IFF_UP;
              getmicrotime(&ifp->if_lastchange);
      
      #ifdef INET6
              /* Userland expects the kernel to set ::1 on default lo(4). */
              if (ifp->if_index == rtable_loindex(ifp->if_rdomain))
                      in6_ifattach(ifp);
      #endif
      
              if_linkstate(ifp);
      }
      
      /*
       * Notify userland, the routing table and hooks owner of
       * a link-state transition.
       */
      void
      if_linkstate_task(void *xifidx)
      {
              unsigned int ifidx = (unsigned long)xifidx;
              struct ifnet *ifp;
      
              KERNEL_LOCK();
              NET_LOCK();
      
              ifp = if_get(ifidx);
              if (ifp != NULL)
                      if_linkstate(ifp);
              if_put(ifp);
      
              NET_UNLOCK();
              KERNEL_UNLOCK();
      }
      
      void
      if_linkstate(struct ifnet *ifp)
      {
    5         NET_ASSERT_LOCKED();
      
              rtm_ifchg(ifp);
              rt_if_track(ifp);
              dohooks(ifp->if_linkstatehooks, 0);
      }
      
      /*
       * Schedule a link state change task.
       */
      void
      if_link_state_change(struct ifnet *ifp)
   40 {
              task_add(net_tq(ifp->if_index), &ifp->if_linkstatetask);
      }
      
      /*
       * Handle interface watchdog timer routine.  Called
       * from softclock, we decrement timer (if set) and
       * call the appropriate interface routine on expiration.
       */
      void
      if_slowtimo(void *arg)
      {
              struct ifnet *ifp = arg;
              int s = splnet();
      
   27         if (ifp->if_watchdog) {
                      if (ifp->if_timer > 0 && --ifp->if_timer == 0)
                              task_add(net_tq(ifp->if_index), &ifp->if_watchdogtask);
                      timeout_add_sec(&ifp->if_slowtimo, IFNET_SLOWTIMO);
              }
              splx(s);
      }
      
      void
      if_watchdog_task(void *xifidx)
      {
              unsigned int ifidx = (unsigned long)xifidx;
              struct ifnet *ifp;
              int s;
      
              ifp = if_get(ifidx);
              if (ifp == NULL)
                      return;
      
              KERNEL_LOCK();
              s = splnet();
              if (ifp->if_watchdog)
                      (*ifp->if_watchdog)(ifp);
              splx(s);
              KERNEL_UNLOCK();
      
              if_put(ifp);
      }
      
      /*
       * Map interface name to interface structure pointer.
       */
      struct ifnet *
      ifunit(const char *name)
    1 {
              struct ifnet *ifp;
      
              KERNEL_ASSERT_LOCKED();
      
   72         TAILQ_FOREACH(ifp, &ifnet, if_list) {
    2                 if (strcmp(ifp->if_xname, name) == 0)
                              return (ifp);
              }
              return (NULL);
      }
      
      /*
       * Map interface index to interface structure pointer.
       */
      struct ifnet *
      if_get(unsigned int index)
  564 {
              struct srp_ref sr;
              struct if_map *if_map;
              struct srp *map;
              struct ifnet *ifp = NULL;
      
              if_map = srp_enter(&sr, &if_idxmap.map);
   23         if (index < if_map->limit) {
                      map = (struct srp *)(if_map + 1);
      
                      ifp = srp_follow(&sr, &map[index]);
   49                 if (ifp != NULL) {
                              KASSERT(ifp->if_index == index);
  533                         if_ref(ifp);
                      }
              }
              srp_leave(&sr);
      
              return (ifp);
      }
      
      struct ifnet *
  533 if_ref(struct ifnet *ifp)
      {
              refcnt_take(&ifp->if_refcnt);
      
              return (ifp);
      }
      
      void
   26 if_put(struct ifnet *ifp)
  574 {
   89         if (ifp == NULL)
                      return;
      
  533         refcnt_rele_wake(&ifp->if_refcnt);
      }
      
      int
      if_setlladdr(struct ifnet *ifp, const uint8_t *lladdr)
      {
              if (ifp->if_sadl == NULL)
                      return (EINVAL);
      
              memcpy(((struct arpcom *)ifp)->ac_enaddr, lladdr, ETHER_ADDR_LEN);
              memcpy(LLADDR(ifp->if_sadl), lladdr, ETHER_ADDR_LEN);
      
              return (0);
      }
      
      int
      if_createrdomain(int rdomain, struct ifnet *ifp)
      {
              int error;
              struct ifnet *loifp;
              char loifname[IFNAMSIZ];
              unsigned int unit = rdomain;
      
              if (!rtable_exists(rdomain) && (error = rtable_add(rdomain)) != 0)
                      return (error);
              if (!rtable_empty(rdomain))
                      return (EEXIST);
      
              /* Create rdomain including its loopback if with unit == rdomain */
              snprintf(loifname, sizeof(loifname), "lo%u", unit);
              error = if_clone_create(loifname, 0);
              if ((loifp = ifunit(loifname)) == NULL)
                      return (ENXIO);
              if (error && (ifp != loifp || error != EEXIST))
                      return (error);
      
              rtable_l2set(rdomain, rdomain, loifp->if_index);
              loifp->if_rdomain = rdomain;
      
              return (0);
      }
      
      int
      if_setrdomain(struct ifnet *ifp, int rdomain)
      {
              struct ifreq ifr;
              int error, up = 0, s;
      
              if (rdomain < 0 || rdomain > RT_TABLEID_MAX)
                      return (EINVAL);
      
              if (rdomain != ifp->if_rdomain &&
                  (ifp->if_flags & IFF_LOOPBACK) &&
                  (ifp->if_index == rtable_loindex(ifp->if_rdomain)))
                      return (EPERM);
      
              if (!rtable_exists(rdomain))
                      return (ESRCH);
      
              /* make sure that the routing table is a real rdomain */
              if (rdomain != rtable_l2(rdomain))
                      return (EINVAL);
      
              if (rdomain != ifp->if_rdomain) {
                      s = splnet();
                      /*
                       * We are tearing down the world.
                       * Take down the IF so:
                       * 1. everything that cares gets a message
                       * 2. the automagic IPv6 bits are recreated
                       */
                      if (ifp->if_flags & IFF_UP) {
                              up = 1;
                              if_down(ifp);
                      }
                      rti_delete(ifp);
      #ifdef MROUTING
                      vif_delete(ifp);
      #endif
                      in_ifdetach(ifp);
      #ifdef INET6
                      in6_ifdetach(ifp);
      #endif
                      splx(s);
              }
      
              /* Let devices like enc(4) or mpe(4) know about the change */
              ifr.ifr_rdomainid = rdomain;
              if ((error = (*ifp->if_ioctl)(ifp, SIOCSIFRDOMAIN,
                  (caddr_t)&ifr)) != ENOTTY)
                      return (error);
              error = 0;
      
              /* Add interface to the specified rdomain */
              ifp->if_rdomain = rdomain;
      
              /* If we took down the IF, bring it back */
              if (up) {
                      s = splnet();
                      if_up(ifp);
                      splx(s);
              }
      
              return (0);
      }
      
      /*
       * Interface ioctls.
       */
      int
      ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p)
   79 {
              struct ifnet *ifp;
              struct ifreq *ifr = (struct ifreq *)data;
              struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
              struct if_afreq *ifar = (struct if_afreq *)data;
              char ifdescrbuf[IFDESCRSIZE];
              char ifrtlabelbuf[RTLABEL_LEN];
              int s, error = 0, oif_xflags;
              size_t bytesdone;
              unsigned short oif_flags;
      
   47         switch (cmd) {
              case SIOCIFCREATE:
    3                 if ((error = suser(p)) != 0)
                              return (error);
    8                 error = if_clone_create(ifr->ifr_name, 0);
                      return (error);
              case SIOCIFDESTROY:
    3                 if ((error = suser(p)) != 0)
                              return (error);
   11                 error = if_clone_destroy(ifr->ifr_name);
                      return (error);
              case SIOCSIFGATTR:
    3                 if ((error = suser(p)) != 0)
                              return (error);
                      NET_LOCK();
    1                 error = if_setgroupattribs(data);
                      NET_UNLOCK();
                      return (error);
              case SIOCGIFCONF:
              case SIOCIFGCLONERS:
              case SIOCGIFGMEMB:
              case SIOCGIFGATTR:
              case SIOCGIFGLIST:
              case SIOCGIFFLAGS:
              case SIOCGIFXFLAGS:
              case SIOCGIFMETRIC:
              case SIOCGIFMTU:
              case SIOCGIFHARDMTU:
              case SIOCGIFDATA:
              case SIOCGIFDESCR:
              case SIOCGIFRTLABEL:
              case SIOCGIFPRIORITY:
              case SIOCGIFRDOMAIN:
              case SIOCGIFGROUP:
              case SIOCGIFLLPRIO:
                      return (ifioctl_get(cmd, data));
              }
      
    3         ifp = ifunit(ifr->ifr_name);
              if (ifp == NULL)
                      return (ENXIO);
              oif_flags = ifp->if_flags;
              oif_xflags = ifp->if_xflags;
      
              switch (cmd) {
              case SIOCIFAFATTACH:
              case SIOCIFAFDETACH:
                      if ((error = suser(p)) != 0)
                              break;
                      NET_LOCK();
                      switch (ifar->ifar_af) {
                      case AF_INET:
                              /* attach is a noop for AF_INET */
                              if (cmd == SIOCIFAFDETACH)
                                      in_ifdetach(ifp);
                              break;
      #ifdef INET6
                      case AF_INET6:
                              if (cmd == SIOCIFAFATTACH)
                                      error = in6_ifattach(ifp);
                              else
                                      in6_ifdetach(ifp);
                              break;
      #endif /* INET6 */
                      default:
                              error = EAFNOSUPPORT;
                      }
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFFLAGS:
                      if ((error = suser(p)) != 0)
                              break;
      
                      NET_LOCK();
                      ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
                              (ifr->ifr_flags & ~IFF_CANTCHANGE);
      
                      error = (*ifp->if_ioctl)(ifp, cmd, data);
                      if (error != 0) {
                              ifp->if_flags = oif_flags;
                      } else if (ISSET(oif_flags ^ ifp->if_flags, IFF_UP)) {
                              s = splnet();
                              if (ISSET(ifp->if_flags, IFF_UP))
                                      if_up(ifp);
                              else
                                      if_down(ifp);
                              splx(s);
                      }
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFXFLAGS:
                      if ((error = suser(p)) != 0)
                              break;
      
                      NET_LOCK();
      #ifdef INET6
                      if (ISSET(ifr->ifr_flags, IFXF_AUTOCONF6)) {
                              error = in6_ifattach(ifp);
                              if (error != 0) {
                                      NET_UNLOCK();
                                      break;
                              }
                      }
      
                      if (ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
                          !ISSET(ifp->if_xflags, IFXF_INET6_NOSOII)) {
                              ifp->if_xflags |= IFXF_INET6_NOSOII;
                              in6_soiiupdate(ifp);
                      }
      
                      if (!ISSET(ifr->ifr_flags, IFXF_INET6_NOSOII) &&
                          ISSET(ifp->if_xflags, IFXF_INET6_NOSOII)) {
                              ifp->if_xflags &= ~IFXF_INET6_NOSOII;
                              in6_soiiupdate(ifp);
                      }
      
      #endif        /* INET6 */
      
      #ifdef MPLS
                      if (ISSET(ifr->ifr_flags, IFXF_MPLS) &&
                          !ISSET(ifp->if_xflags, IFXF_MPLS)) {
                              s = splnet();
                              ifp->if_xflags |= IFXF_MPLS;
                              ifp->if_ll_output = ifp->if_output;
                              ifp->if_output = mpls_output;
                              splx(s);
                      }
                      if (ISSET(ifp->if_xflags, IFXF_MPLS) &&
                          !ISSET(ifr->ifr_flags, IFXF_MPLS)) {
                              s = splnet();
                              ifp->if_xflags &= ~IFXF_MPLS;
                              ifp->if_output = ifp->if_ll_output;
                              ifp->if_ll_output = NULL;
                              splx(s);
                      }
      #endif        /* MPLS */
      
      #ifndef SMALL_KERNEL
                      if (ifp->if_capabilities & IFCAP_WOL) {
                              if (ISSET(ifr->ifr_flags, IFXF_WOL) &&
                                  !ISSET(ifp->if_xflags, IFXF_WOL)) {
                                      s = splnet();
                                      ifp->if_xflags |= IFXF_WOL;
                                      error = ifp->if_wol(ifp, 1);
                                      splx(s);
                              }
                              if (ISSET(ifp->if_xflags, IFXF_WOL) &&
                                  !ISSET(ifr->ifr_flags, IFXF_WOL)) {
                                      s = splnet();
                                      ifp->if_xflags &= ~IFXF_WOL;
                                      error = ifp->if_wol(ifp, 0);
                                      splx(s);
                              }
                      } else if (ISSET(ifr->ifr_flags, IFXF_WOL)) {
                              ifr->ifr_flags &= ~IFXF_WOL;
                              error = ENOTSUP;
                      }
      #endif
      
                      if (error == 0)
                              ifp->if_xflags = (ifp->if_xflags & IFXF_CANTCHANGE) |
                                      (ifr->ifr_flags & ~IFXF_CANTCHANGE);
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFMETRIC:
                      if ((error = suser(p)) != 0)
                              break;
                      NET_LOCK();
                      ifp->if_metric = ifr->ifr_metric;
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFMTU:
                      if ((error = suser(p)) != 0)
                              break;
                      NET_LOCK();
                      error = (*ifp->if_ioctl)(ifp, cmd, data);
                      NET_UNLOCK();
                      if (!error)
                              rtm_ifchg(ifp);
                      break;
      
              case SIOCSIFDESCR:
                      if ((error = suser(p)) != 0)
                              break;
                      error = copyinstr(ifr->ifr_data, ifdescrbuf,
                          IFDESCRSIZE, &bytesdone);
                      if (error == 0) {
                              (void)memset(ifp->if_description, 0, IFDESCRSIZE);
                              strlcpy(ifp->if_description, ifdescrbuf, IFDESCRSIZE);
                      }
                      break;
      
              case SIOCSIFRTLABEL:
                      if ((error = suser(p)) != 0)
                              break;
                      error = copyinstr(ifr->ifr_data, ifrtlabelbuf,
                          RTLABEL_LEN, &bytesdone);
                      if (error == 0) {
                              rtlabel_unref(ifp->if_rtlabelid);
                              ifp->if_rtlabelid = rtlabel_name2id(ifrtlabelbuf);
                      }
                      break;
      
              case SIOCSIFPRIORITY:
                      if ((error = suser(p)) != 0)
                              break;
                      if (ifr->ifr_metric < 0 || ifr->ifr_metric > 15) {
                              error = EINVAL;
                              break;
                      }
                      ifp->if_priority = ifr->ifr_metric;
                      break;
      
              case SIOCSIFRDOMAIN:
                      if ((error = suser(p)) != 0)
                              break;
                      error = if_createrdomain(ifr->ifr_rdomainid, ifp);
                      if (!error || error == EEXIST) {
                              NET_LOCK();
                              error = if_setrdomain(ifp, ifr->ifr_rdomainid);
                              NET_UNLOCK();
                      }
                      break;
      
              case SIOCAIFGROUP:
                      if ((error = suser(p)))
                              break;
                      NET_LOCK();
                      error = if_addgroup(ifp, ifgr->ifgr_group);
                      if (error == 0) {
                              error = (*ifp->if_ioctl)(ifp, cmd, data);
                              if (error == ENOTTY)
                                      error = 0;
                      }
                      NET_UNLOCK();
                      break;
      
              case SIOCDIFGROUP:
                      if ((error = suser(p)))
                              break;
                      NET_LOCK();
                      error = (*ifp->if_ioctl)(ifp, cmd, data);
                      if (error == ENOTTY)
                              error = 0;
                      if (error == 0)
                              error = if_delgroup(ifp, ifgr->ifgr_group);
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFLLADDR:
                      if ((error = suser(p)))
                              break;
                      if ((ifp->if_sadl == NULL) ||
                          (ifr->ifr_addr.sa_len != ETHER_ADDR_LEN) ||
                          (ETHER_IS_MULTICAST(ifr->ifr_addr.sa_data))) {
                              error = EINVAL;
                              break;
                      }
                      NET_LOCK();
                      switch (ifp->if_type) {
                      case IFT_ETHER:
                      case IFT_CARP:
                      case IFT_XETHER:
                      case IFT_ISO88025:
                              error = (*ifp->if_ioctl)(ifp, cmd, data);
                              if (error == ENOTTY)
                                      error = 0;
                              if (error == 0)
                                      error = if_setlladdr(ifp,
                                          ifr->ifr_addr.sa_data);
                              break;
                      default:
                              error = ENODEV;
                      }
      
                      if (error == 0)
                              ifnewlladdr(ifp);
                      NET_UNLOCK();
                      break;
      
              case SIOCSIFLLPRIO:
                      if ((error = suser(p)))
                              break;
                      if (ifr->ifr_llprio < IFQ_MINPRIO ||
                          ifr->ifr_llprio > IFQ_MAXPRIO) {
                              error = EINVAL;
                              break;
                      }
                      NET_LOCK();
                      ifp->if_llprio = ifr->ifr_llprio;
                      NET_UNLOCK();
                      break;
      
              case SIOCGIFSFFPAGE:
                      error = suser(p);
                      if (error != 0)
                              break;
      
                      error = if_sffpage_check(data);
                      if (error != 0)
                              break;
      
                      /* don't take NET_LOCK because i2c reads take a long time */
                      error = ((*ifp->if_ioctl)(ifp, cmd, data));
                      break;
      
              case SIOCSETKALIVE:
              case SIOCDIFPHYADDR:
              case SIOCSLIFPHYADDR:
              case SIOCSLIFPHYRTABLE:
              case SIOCSLIFPHYTTL:
              case SIOCSLIFPHYDF:
              case SIOCSLIFPHYECN:
              case SIOCADDMULTI:
              case SIOCDELMULTI:
              case SIOCSIFMEDIA:
              case SIOCSVNETID:
              case SIOCSVNETFLOWID:
              case SIOCSTXHPRIO:
              case SIOCSRXHPRIO:
              case SIOCSIFPAIR:
              case SIOCSIFPARENT:
              case SIOCDIFPARENT:
              case SIOCSETMPWCFG:
              case SIOCSETLABEL:
              case SIOCDELLABEL:
              case SIOCSPWE3CTRLWORD:
              case SIOCSPWE3FAT:
              case SIOCSPWE3NEIGHBOR:
              case SIOCDPWE3NEIGHBOR:
                      if ((error = suser(p)) != 0)
                              break;
                      /* FALLTHROUGH */
              default:
                      error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
                              (struct mbuf *) cmd, (struct mbuf *) data,
                              (struct mbuf *) ifp, p));
                      if (error == EOPNOTSUPP) {
                              NET_LOCK();
                              error = ((*ifp->if_ioctl)(ifp, cmd, data));
                              NET_UNLOCK();
                      }
                      break;
              }
      
              if (oif_flags != ifp->if_flags || oif_xflags != ifp->if_xflags)
                      rtm_ifchg(ifp);
      
              if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0)
                      getmicrotime(&ifp->if_lastchange);
      
              return (error);
      }
      
      int
      ifioctl_get(u_long cmd, caddr_t data)
   47 {
              struct ifnet *ifp;
              struct ifreq *ifr = (struct ifreq *)data;
              char ifdescrbuf[IFDESCRSIZE];
              char ifrtlabelbuf[RTLABEL_LEN];
              int error = 0;
              size_t bytesdone;
              const char *label;
      
              switch(cmd) {
              case SIOCGIFCONF:
    6                 NET_RLOCK();
                      error = ifconf(data);
                      NET_RUNLOCK();
                      return (error);
              case SIOCIFGCLONERS:
   13                 error = if_clone_list((struct if_clonereq *)data);
                      return (error);
              case SIOCGIFGMEMB:
    1                 NET_RLOCK();
                      error = if_getgroupmembers(data);
                      NET_RUNLOCK();
                      return (error);
              case SIOCGIFGATTR:
                      NET_RLOCK();
    1                 error = if_getgroupattribs(data);
                      NET_RUNLOCK();
                      return (error);
              case SIOCGIFGLIST:
                      NET_RLOCK();
    6                 error = if_getgrouplist(data);
                      NET_RUNLOCK();
                      return (error);
              }
      
   20         ifp = ifunit(ifr->ifr_name);
              if (ifp == NULL)
                      return (ENXIO);
      
              NET_RLOCK();
      
              switch(cmd) {
              case SIOCGIFFLAGS:
                      ifr->ifr_flags = ifp->if_flags;
    1                 if (ifq_is_oactive(&ifp->if_snd))
                              ifr->ifr_flags |= IFF_OACTIVE;
                      break;
      
              case SIOCGIFXFLAGS:
    1                 ifr->ifr_flags = ifp->if_xflags & ~(IFXF_MPSAFE|IFXF_CLONED);
                      break;
      
              case SIOCGIFMETRIC:
    1                 ifr->ifr_metric = ifp->if_metric;
                      break;
      
              case SIOCGIFMTU:
    1                 ifr->ifr_mtu = ifp->if_mtu;
                      break;
      
              case SIOCGIFHARDMTU:
    1                 ifr->ifr_hardmtu = ifp->if_hardmtu;
                      break;
      
              case SIOCGIFDATA: {
    2                 struct if_data ifdata;
                      if_getdata(ifp, &ifdata);
                      error = copyout(&ifdata, ifr->ifr_data, sizeof(ifdata));
                      break;
              }
      
              case SIOCGIFDESCR:
    1                 strlcpy(ifdescrbuf, ifp->if_description, IFDESCRSIZE);
                      error = copyoutstr(ifdescrbuf, ifr->ifr_data, IFDESCRSIZE,
                          &bytesdone);
                      break;
      
              case SIOCGIFRTLABEL:
    1                 if (ifp->if_rtlabelid &&
                          (label = rtlabel_id2name(ifp->if_rtlabelid)) != NULL) {
                              strlcpy(ifrtlabelbuf, label, RTLABEL_LEN);
                              error = copyoutstr(ifrtlabelbuf, ifr->ifr_data,
                                  RTLABEL_LEN, &bytesdone);
                      } else
                              error = ENOENT;
                      break;
      
              case SIOCGIFPRIORITY:
    1                 ifr->ifr_metric = ifp->if_priority;
                      break;
      
              case SIOCGIFRDOMAIN:
    1                 ifr->ifr_rdomainid = ifp->if_rdomain;
                      break;
      
              case SIOCGIFGROUP:
    6                 error = if_getgroup(data, ifp);
                      break;
      
              case SIOCGIFLLPRIO:
    1                 ifr->ifr_llprio = ifp->if_llprio;
                      break;
      
              default:
                      panic("invalid ioctl %lu", cmd);
              }
      
              NET_RUNLOCK();
      
              return (error);
      }
      
      static int
      if_sffpage_check(const caddr_t data)
      {
              const struct if_sffpage *sff = (const struct if_sffpage *)data;
      
              switch (sff->sff_addr) {
              case IFSFF_ADDR_EEPROM:
              case IFSFF_ADDR_DDM:
                      break;
              default:
                      return (EINVAL);
              }
      
              return (0);
      }
      
      int
      if_txhprio_l2_check(int hdrprio)
      {
              switch (hdrprio) {
              case IF_HDRPRIO_PACKET:
                      return (0);
              default:
                      if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
                              return (0);
                      break;
              }
      
              return (EINVAL);
      }
      
      int
      if_txhprio_l3_check(int hdrprio)
      {
              switch (hdrprio) {
              case IF_HDRPRIO_PACKET:
              case IF_HDRPRIO_PAYLOAD:
                      return (0);
              default:
                      if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
                              return (0);
                      break;
              }
      
              return (EINVAL);
      }
      
      int
      if_rxhprio_l2_check(int hdrprio)
      {
              switch (hdrprio) {
              case IF_HDRPRIO_PACKET:
              case IF_HDRPRIO_OUTER:
                      return (0);
              default:
                      if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
                              return (0);
                      break;
              }
      
              return (EINVAL);
      }
      
      int
      if_rxhprio_l3_check(int hdrprio)
      {
              switch (hdrprio) {
              case IF_HDRPRIO_PACKET:
              case IF_HDRPRIO_PAYLOAD:
              case IF_HDRPRIO_OUTER:
                      return (0);
              default:
                      if (hdrprio >= IF_HDRPRIO_MIN && hdrprio <= IF_HDRPRIO_MAX)
                              return (0);
                      break;
              }
      
              return (EINVAL);
      }
      
      /*
       * Return interface configuration
       * of system.  List may be used
       * in later ioctl's (above) to get
       * other information.
       */
      int
      ifconf(caddr_t data)
    6 {
              struct ifconf *ifc = (struct ifconf *)data;
              struct ifnet *ifp;
              struct ifaddr *ifa;
              struct ifreq ifr, *ifrp;
              int space = ifc->ifc_len, error = 0;
      
              /* If ifc->ifc_len is 0, fill it in with the needed size and return. */
              if (space == 0) {
    4                 TAILQ_FOREACH(ifp, &ifnet, if_list) {
                              struct sockaddr *sa;
      
                              if (TAILQ_EMPTY(&ifp->if_addrlist))
    4                                 space += sizeof (ifr);
                              else
    4                                 TAILQ_FOREACH(ifa,
                                          &ifp->if_addrlist, ifa_list) {
                                              sa = ifa->ifa_addr;
                                              if (sa->sa_len > sizeof(*sa))
                                                      space += sa->sa_len -
                                                          sizeof(*sa);
                                              space += sizeof(ifr);
                                      }
                      }
                      ifc->ifc_len = space;
                      return (0);
              }
      
              ifrp = ifc->ifc_req;
    5         TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (space < sizeof(ifr))
                              break;
                      bcopy(ifp->if_xname, ifr.ifr_name, IFNAMSIZ);
                      if (TAILQ_EMPTY(&ifp->if_addrlist)) {
                              bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
                              error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
                                  sizeof(ifr));
    2                         if (error)
                                      break;
    2                         space -= sizeof (ifr), ifrp++;
                      } else
    2                         TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                                      struct sockaddr *sa = ifa->ifa_addr;
      
                                      if (space < sizeof(ifr))
                                              break;
                                      if (sa->sa_len <= sizeof(*sa)) {
    4                                         ifr.ifr_addr = *sa;
                                              error = copyout((caddr_t)&ifr,
                                                  (caddr_t)ifrp, sizeof (ifr));
                                              ifrp++;
                                      } else {
                                              space -= sa->sa_len - sizeof(*sa);
    2                                         if (space < sizeof (ifr))
                                                      break;
                                              error = copyout((caddr_t)&ifr,
                                                  (caddr_t)ifrp,
                                                  sizeof(ifr.ifr_name));
    2                                         if (error == 0)
                                                      error = copyout((caddr_t)sa,
                                                          (caddr_t)&ifrp->ifr_addr,
    2                                                     sa->sa_len);
                                              ifrp = (struct ifreq *)(sa->sa_len +
                                                  (caddr_t)&ifrp->ifr_addr);
                                      }
                                      if (error)
                                              break;
                                      space -= sizeof (ifr);
                              }
              }
              ifc->ifc_len -= space;
              return (error);
      }
      
      void
      if_counters_alloc(struct ifnet *ifp)
      {
              KASSERT(ifp->if_counters == NULL);
      
              ifp->if_counters = counters_alloc(ifc_ncounters);
      }
      
      void
      if_counters_free(struct ifnet *ifp)
      {
              KASSERT(ifp->if_counters != NULL);
      
              counters_free(ifp->if_counters, ifc_ncounters);
              ifp->if_counters = NULL;
      }
      
      void
      if_getdata(struct ifnet *ifp, struct if_data *data)
    7 {
              unsigned int i;
      
              *data = ifp->if_data;
      
    7         if (ifp->if_counters != NULL) {
                      uint64_t counters[ifc_ncounters];
      
                      counters_read(ifp->if_counters, counters, nitems(counters));
      
                      data->ifi_ipackets += counters[ifc_ipackets];
                      data->ifi_ierrors += counters[ifc_ierrors];
                      data->ifi_opackets += counters[ifc_opackets];
                      data->ifi_oerrors += counters[ifc_oerrors];
                      data->ifi_collisions += counters[ifc_collisions];
                      data->ifi_ibytes += counters[ifc_ibytes];
                      data->ifi_obytes += counters[ifc_obytes];
                      data->ifi_imcasts += counters[ifc_imcasts];
                      data->ifi_omcasts += counters[ifc_omcasts];
                      data->ifi_iqdrops += counters[ifc_iqdrops];
                      data->ifi_oqdrops += counters[ifc_oqdrops];
                      data->ifi_noproto += counters[ifc_noproto];
              }
      
    7         for (i = 0; i < ifp->if_nifqs; i++) {
                      struct ifqueue *ifq = ifp->if_ifqs[i];
      
                      ifq_add_data(ifq, data);
              }
      
    7         for (i = 0; i < ifp->if_niqs; i++) {
                      struct ifiqueue *ifiq = ifp->if_iqs[i];
      
                      ifiq_add_data(ifiq, data);
              }
      }
      
      /*
       * Dummy functions replaced in ifnet during detach (if protocols decide to
       * fiddle with the if during detach.
       */
      void
      if_detached_qstart(struct ifqueue *ifq)
      {
              ifq_purge(ifq);
      }
      
      int
      if_detached_ioctl(struct ifnet *ifp, u_long a, caddr_t b)
      {
              return ENODEV;
      }
      
      /*
       * Create interface group without members
       */
      struct ifg_group *
      if_creategroup(const char *groupname)
      {
              struct ifg_group        *ifg;
      
              if ((ifg = malloc(sizeof(*ifg), M_TEMP, M_NOWAIT)) == NULL)
                      return (NULL);
      
   16         strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group));
              ifg->ifg_refcnt = 0;
              ifg->ifg_carp_demoted = 0;
              TAILQ_INIT(&ifg->ifg_members);
      #if NPF > 0
              pfi_attach_ifgroup(ifg);
      #endif
              TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next);
      
              return (ifg);
      }
      
      /*
       * Add a group to an interface
       */
      int
      if_addgroup(struct ifnet *ifp, const char *groupname)
   27 {
              struct ifg_list                *ifgl;
              struct ifg_group        *ifg = NULL;
              struct ifg_member        *ifgm;
      
   27         if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' &&
                  groupname[strlen(groupname) - 1] <= '9')
                      return (EINVAL);
      
   27         TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
                      if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
                              return (EEXIST);
      
              if ((ifgl = malloc(sizeof(*ifgl), M_TEMP, M_NOWAIT)) == NULL)
                      return (ENOMEM);
      
              if ((ifgm = malloc(sizeof(*ifgm), M_TEMP, M_NOWAIT)) == NULL) {
                      free(ifgl, M_TEMP, sizeof(*ifgl));
                      return (ENOMEM);
              }
      
   27         TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
   27                 if (!strcmp(ifg->ifg_group, groupname))
                              break;
      
   16         if (ifg == NULL && (ifg = if_creategroup(groupname)) == NULL) {
                      free(ifgl, M_TEMP, sizeof(*ifgl));
                      free(ifgm, M_TEMP, sizeof(*ifgm));
                      return (ENOMEM);
              }
      
              ifg->ifg_refcnt++;
              ifgl->ifgl_group = ifg;
              ifgm->ifgm_ifp = ifp;
      
              TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
              TAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next);
      
      #if NPF > 0
              pfi_group_addmember(groupname, ifp);
      #endif
      
              return (0);
      }
      
      /*
       * Remove a group from an interface
       */
      int
      if_delgroup(struct ifnet *ifp, const char *groupname)
   20 {
              struct ifg_list                *ifgl;
              struct ifg_member        *ifgm;
      
              TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
                      if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
                              break;
              if (ifgl == NULL)
                      return (ENOENT);
      
              TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next);
      
              TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next)
   20                 if (ifgm->ifgm_ifp == ifp)
                              break;
      
              if (ifgm != NULL) {
   20                 TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next);
                      free(ifgm, M_TEMP, sizeof(*ifgm));
              }
      
   20         if (--ifgl->ifgl_group->ifg_refcnt == 0) {
    7                 TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next);
      #if NPF > 0
                      pfi_detach_ifgroup(ifgl->ifgl_group);
      #endif
                      free(ifgl->ifgl_group, M_TEMP, 0);
              }
      
              free(ifgl, M_TEMP, sizeof(*ifgl));
      
      #if NPF > 0
              pfi_group_change(groupname);
      #endif
      
              return (0);
      }
      
      /*
       * Stores all groups from an interface in memory pointed
       * to by data
       */
      int
      if_getgroup(caddr_t data, struct ifnet *ifp)
      {
              int                         len, error;
              struct ifg_list                *ifgl;
              struct ifg_req                 ifgrq, *ifgp;
              struct ifgroupreq        *ifgr = (struct ifgroupreq *)data;
      
              if (ifgr->ifgr_len == 0) {
    1                 TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
                              ifgr->ifgr_len += sizeof(struct ifg_req);
                      return (0);
              }
      
              len = ifgr->ifgr_len;
              ifgp = ifgr->ifgr_groups;
    2         TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
    2                 if (len < sizeof(ifgrq))
                              return (EINVAL);
                      bzero(&ifgrq, sizeof ifgrq);
                      strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
                          sizeof(ifgrq.ifgrq_group));
    2                 if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
                          sizeof(struct ifg_req))))
                              return (error);
                      len -= sizeof(ifgrq);
                      ifgp++;
              }
      
              return (0);
      }
      
      /*
       * Stores all members of a group in memory pointed to by data
       */
      int
      if_getgroupmembers(caddr_t data)
    1 {
              struct ifgroupreq        *ifgr = (struct ifgroupreq *)data;
              struct ifg_group        *ifg;
              struct ifg_member        *ifgm;
              struct ifg_req                 ifgrq, *ifgp;
              int                         len, error;
      
    1         TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
                      if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
                              break;
              if (ifg == NULL)
                      return (ENOENT);
      
              if (ifgr->ifgr_len == 0) {
                      TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
                              ifgr->ifgr_len += sizeof(ifgrq);
                      return (0);
              }
      
              len = ifgr->ifgr_len;
              ifgp = ifgr->ifgr_groups;
              TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) {
                      if (len < sizeof(ifgrq))
                              return (EINVAL);
                      bzero(&ifgrq, sizeof ifgrq);
                      strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname,
                          sizeof(ifgrq.ifgrq_member));
                      if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
                          sizeof(struct ifg_req))))
                              return (error);
                      len -= sizeof(ifgrq);
                      ifgp++;
              }
      
              return (0);
      }
      
      int
      if_getgroupattribs(caddr_t data)
      {
              struct ifgroupreq        *ifgr = (struct ifgroupreq *)data;
              struct ifg_group        *ifg;
      
    1         TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
                      if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
                              break;
              if (ifg == NULL)
                      return (ENOENT);
      
              ifgr->ifgr_attrib.ifg_carp_demoted = ifg->ifg_carp_demoted;
      
              return (0);
      }
      
      int
      if_setgroupattribs(caddr_t data)
      {
              struct ifgroupreq        *ifgr = (struct ifgroupreq *)data;
              struct ifg_group        *ifg;
              struct ifg_member        *ifgm;
              int                         demote;
      
    1         TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
                      if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
                              break;
              if (ifg == NULL)
                      return (ENOENT);
      
              demote = ifgr->ifgr_attrib.ifg_carp_demoted;
              if (demote + ifg->ifg_carp_demoted > 0xff ||
                  demote + ifg->ifg_carp_demoted < 0)
                      return (EINVAL);
      
              ifg->ifg_carp_demoted += demote;
      
              TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
                      ifgm->ifgm_ifp->if_ioctl(ifgm->ifgm_ifp, SIOCSIFGATTR, data);
      
              return (0);
      }
      
      /*
       * Stores all groups in memory pointed to by data
       */
      int
      if_getgrouplist(caddr_t data)
      {
              struct ifgroupreq        *ifgr = (struct ifgroupreq *)data;
              struct ifg_group        *ifg;
              struct ifg_req                 ifgrq, *ifgp;
              int                         len, error;
      
              if (ifgr->ifgr_len == 0) {
    1                 TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
                              ifgr->ifgr_len += sizeof(ifgrq);
                      return (0);
              }
      
              len = ifgr->ifgr_len;
              ifgp = ifgr->ifgr_groups;
    2         TAILQ_FOREACH(ifg, &ifg_head, ifg_next) {
    2                 if (len < sizeof(ifgrq))
                              return (EINVAL);
                      bzero(&ifgrq, sizeof ifgrq);
                      strlcpy(ifgrq.ifgrq_group, ifg->ifg_group,
                          sizeof(ifgrq.ifgrq_group));
    2                 if ((error = copyout((caddr_t)&ifgrq, (caddr_t)ifgp,
                          sizeof(struct ifg_req))))
                              return (error);
                      len -= sizeof(ifgrq);
                      ifgp++;
              }
      
              return (0);
      }
      
      void
      if_group_routechange(struct sockaddr *dst, struct sockaddr *mask)
   13 {
              switch (dst->sa_family) {
              case AF_INET:
    3                 if (satosin(dst)->sin_addr.s_addr == INADDR_ANY &&
                          mask && (mask->sa_len == 0 ||
                          satosin(mask)->sin_addr.s_addr == INADDR_ANY))
                              if_group_egress_build();
                      break;
      #ifdef INET6
              case AF_INET6:
                      if (IN6_ARE_ADDR_EQUAL(&(satosin6(dst))->sin6_addr,
   13                     &in6addr_any) && mask && (mask->sa_len == 0 ||
                          IN6_ARE_ADDR_EQUAL(&(satosin6(mask))->sin6_addr,
                          &in6addr_any)))
                              if_group_egress_build();
                      break;
      #endif
              }
      }
      
      int
      if_group_egress_build(void)
      {
              struct ifnet                *ifp;
              struct ifg_group        *ifg;
              struct ifg_member        *ifgm, *next;
              struct sockaddr_in         sa_in;
      #ifdef INET6
              struct sockaddr_in6         sa_in6;
      #endif
              struct rtentry                *rt;
      
              TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
                      if (!strcmp(ifg->ifg_group, IFG_EGRESS))
                              break;
      
              if (ifg != NULL)
                      TAILQ_FOREACH_SAFE(ifgm, &ifg->ifg_members, ifgm_next, next)
                              if_delgroup(ifgm->ifgm_ifp, IFG_EGRESS);
      
              bzero(&sa_in, sizeof(sa_in));
              sa_in.sin_len = sizeof(sa_in);
              sa_in.sin_family = AF_INET;
              rt = rtable_lookup(0, sintosa(&sa_in), sintosa(&sa_in), NULL, RTP_ANY);
              while (rt != NULL) {
                      ifp = if_get(rt->rt_ifidx);
                      if (ifp != NULL) {
                              if_addgroup(ifp, IFG_EGRESS);
                              if_put(ifp);
                      }
                      rt = rtable_iterate(rt);
              }
      
      #ifdef INET6
              bcopy(&sa6_any, &sa_in6, sizeof(sa_in6));
              rt = rtable_lookup(0, sin6tosa(&sa_in6), sin6tosa(&sa_in6), NULL,
                  RTP_ANY);
              while (rt != NULL) {
                      ifp = if_get(rt->rt_ifidx);
                      if (ifp != NULL) {
                              if_addgroup(ifp, IFG_EGRESS);
                              if_put(ifp);
                      }
                      rt = rtable_iterate(rt);
              }
      #endif /* INET6 */
      
              return (0);
      }
      
      /*
       * Set/clear promiscuous mode on interface ifp based on the truth value
       * of pswitch.  The calls are reference counted so that only the first
       * "on" request actually has an effect, as does the final "off" request.
       * Results are undefined if the "off" and "on" requests are not matched.
       */
      int
      ifpromisc(struct ifnet *ifp, int pswitch)
    5 {
              struct ifreq ifr;
              unsigned short oif_flags;
              int oif_pcount, error;
      
              oif_flags = ifp->if_flags;
              oif_pcount = ifp->if_pcount;
              if (pswitch) {
    1                 if (ifp->if_pcount++ != 0)
                              return (0);
    2                 ifp->if_flags |= IFF_PROMISC;
              } else {
    1                 if (--ifp->if_pcount > 0)
                              return (0);
    2                 ifp->if_flags &= ~IFF_PROMISC;
              }
      
    1         if ((ifp->if_flags & IFF_UP) == 0)
                      return (0);
      
              memset(&ifr, 0, sizeof(ifr));
              ifr.ifr_flags = ifp->if_flags;
              error = ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
    3         if (error) {
                      ifp->if_flags = oif_flags;
                      ifp->if_pcount = oif_pcount;
              }
      
              return (error);
      }
      
      void
      ifa_add(struct ifnet *ifp, struct ifaddr *ifa)
      {
              TAILQ_INSERT_TAIL(&ifp->if_addrlist, ifa, ifa_list);
      }
      
      void
      ifa_del(struct ifnet *ifp, struct ifaddr *ifa)
    3 {
              TAILQ_REMOVE(&ifp->if_addrlist, ifa, ifa_list);
      }
      
      void
      ifa_update_broadaddr(struct ifnet *ifp, struct ifaddr *ifa, struct sockaddr *sa)
      {
              if (ifa->ifa_broadaddr->sa_len != sa->sa_len)
                      panic("ifa_update_broadaddr does not support dynamic length");
              bcopy(sa, ifa->ifa_broadaddr, sa->sa_len);
      }
      
      #ifdef DDB
      /* debug function, can be called from ddb> */
      void
      ifa_print_all(void)
      {
              struct ifnet *ifp;
              struct ifaddr *ifa;
      
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                              char addr[INET6_ADDRSTRLEN];
      
                              switch (ifa->ifa_addr->sa_family) {
                              case AF_INET:
                                      printf("%s", inet_ntop(AF_INET,
                                          &satosin(ifa->ifa_addr)->sin_addr,
                                          addr, sizeof(addr)));
                                      break;
      #ifdef INET6
                              case AF_INET6:
                                      printf("%s", inet_ntop(AF_INET6,
                                          &(satosin6(ifa->ifa_addr))->sin6_addr,
                                          addr, sizeof(addr)));
                                      break;
      #endif
                              }
                              printf(" on %s\n", ifp->if_xname);
                      }
              }
      }
      #endif /* DDB */
      
      void
      ifnewlladdr(struct ifnet *ifp)
      {
      #ifdef INET6
              struct ifaddr *ifa;
      #endif
              struct ifreq ifrq;
              short up;
              int s;
      
              s = splnet();
              up = ifp->if_flags & IFF_UP;
      
              if (up) {
                      /* go down for a moment... */
                      ifp->if_flags &= ~IFF_UP;
                      ifrq.ifr_flags = ifp->if_flags;
                      (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
              }
      
              ifp->if_flags |= IFF_UP;
              ifrq.ifr_flags = ifp->if_flags;
              (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
      
      #ifdef INET6
              /*
               * Update the link-local address.  Don't do it if we're
               * a router to avoid confusing hosts on the network.
               */
              if (!ip6_forwarding) {
                      ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa;
                      if (ifa) {
                              in6_purgeaddr(ifa);
                              dohooks(ifp->if_addrhooks, 0);
                              in6_ifattach(ifp);
                      }
              }
      #endif
              if (!up) {
                      /* go back down */
                      ifp->if_flags &= ~IFF_UP;
                      ifrq.ifr_flags = ifp->if_flags;
                      (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifrq);
              }
              splx(s);
      }
      
      int net_ticks;
      u_int net_livelocks;
      
      void
      net_tick(void *null)
      {
              extern int ticks;
      
              if (ticks - net_ticks > 1)
                      net_livelocks++;
      
              net_ticks = ticks;
      
              timeout_add(&net_tick_to, 1);
      }
      
      int
      net_livelocked(void)
      {
              extern int ticks;
      
              return (ticks - net_ticks > 1);
      }
      
      void
      if_rxr_init(struct if_rxring *rxr, u_int lwm, u_int hwm)
      {
              extern int ticks;
      
              memset(rxr, 0, sizeof(*rxr));
      
              rxr->rxr_adjusted = ticks;
              rxr->rxr_cwm = rxr->rxr_lwm = lwm;
              rxr->rxr_hwm = hwm;
      }
      
      static inline void
      if_rxr_adjust_cwm(struct if_rxring *rxr)
      {
              extern int ticks;
      
              if (net_livelocked()) {
                      if (rxr->rxr_cwm > rxr->rxr_lwm)
                              rxr->rxr_cwm--;
                      else
                              return;
              } else if (rxr->rxr_alive >= rxr->rxr_lwm)
                      return;
              else if (rxr->rxr_cwm < rxr->rxr_hwm)
                      rxr->rxr_cwm++;
      
              rxr->rxr_adjusted = ticks;
      }
      
      void
      if_rxr_livelocked(struct if_rxring *rxr)
      {
              extern int ticks;
      
              if (ticks - rxr->rxr_adjusted >= 1) {
                      if (rxr->rxr_cwm > rxr->rxr_lwm)
                              rxr->rxr_cwm--;
      
                      rxr->rxr_adjusted = ticks;
              }
      }
      
      u_int
      if_rxr_get(struct if_rxring *rxr, u_int max)
      {
              extern int ticks;
              u_int diff;
      
              if (ticks - rxr->rxr_adjusted >= 1) {
                      /* we're free to try for an adjustment */
                      if_rxr_adjust_cwm(rxr);
              }
      
              if (rxr->rxr_alive >= rxr->rxr_cwm)
                      return (0);
      
              diff = min(rxr->rxr_cwm - rxr->rxr_alive, max);
              rxr->rxr_alive += diff;
      
              return (diff);
      }
      
      int
      if_rxr_info_ioctl(struct if_rxrinfo *uifri, u_int t, struct if_rxring_info *e)
      {
              struct if_rxrinfo kifri;
              int error;
              u_int n;
      
              error = copyin(uifri, &kifri, sizeof(kifri));
              if (error)
                      return (error);
      
              n = min(t, kifri.ifri_total);
              kifri.ifri_total = t;
      
              if (n > 0) {
                      error = copyout(e, kifri.ifri_entries, sizeof(*e) * n);
                      if (error)
                              return (error);
              }
      
              return (copyout(&kifri, uifri, sizeof(kifri)));
      }
      
      int
      if_rxr_ioctl(struct if_rxrinfo *ifri, const char *name, u_int size,
          struct if_rxring *rxr)
      {
              struct if_rxring_info ifr;
      
              memset(&ifr, 0, sizeof(ifr));
      
              if (name != NULL)
                      strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
      
              ifr.ifr_size = size;
              ifr.ifr_info = *rxr;
      
              return (if_rxr_info_ioctl(ifri, 1, &ifr));
      }
      
      /*
       * Network stack input queues.
       */
      
      void
      niq_init(struct niqueue *niq, u_int maxlen, u_int isr)
      {
              mq_init(&niq->ni_q, maxlen, IPL_NET);
              niq->ni_isr = isr;
      }
      
      int
      niq_enqueue(struct niqueue *niq, struct mbuf *m)
      {
              int rv;
      
              rv = mq_enqueue(&niq->ni_q, m);
              if (rv == 0)
                      schednetisr(niq->ni_isr);
              else
                      if_congestion();
      
              return (rv);
      }
      
      int
      niq_enlist(struct niqueue *niq, struct mbuf_list *ml)
      {
              int rv;
      
              rv = mq_enlist(&niq->ni_q, ml);
              if (rv == 0)
                      schednetisr(niq->ni_isr);
              else
                      if_congestion();
      
              return (rv);
      }
      
      __dead void
      unhandled_af(int af)
      {
              panic("unhandled af %d", af);
      }
      
      /*
       * XXXSMP This tunable is here to work around the fact that IPsec
       * globals aren't ready to be accessed by multiple threads in
       * parallel.
       */
      int                 nettaskqs = NET_TASKQ;
      
      struct taskq *
      net_tq(unsigned int ifindex)
   61 {
              struct taskq *t = NULL;
      
              t = nettqmp[ifindex % nettaskqs];
      
              return (t);
      }
      /*        $OpenBSD: ip_id.c,v 1.24 2014/11/18 02:37:31 tedu Exp $ */
      
      /*
       * Copyright (c) 2008 Theo de Raadt, Ryan McBride
       * 
       * Slightly different algorithm from the one designed by
       * Matthew Dillon <dillon@backplane.com> for The DragonFly Project
       * 
       * Permission to use, copy, modify, and distribute this software for any
       * purpose with or without fee is hereby granted, provided that the above
       * copyright notice and this permission notice appear in all copies.
       *
       * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       */
      
      /*
       * Random ip sequence number generator.  Use the system PRNG to shuffle
       * the 65536 entry ID space.  We reshuffle the ID we pick out of the array
       * into the previous 32767 cells, providing an guarantee that an ID will not
       * be reused for at least 32768 calls.
       */
      #include <sys/param.h>
      #include <sys/systm.h>
      
      static u_int16_t ip_shuffle[65536];
      static int isindex = 0;
      
      u_int16_t ip_randomid(void);
      
      /*
       * Return a random IP id.  Shuffle the new value we get into the previous half
       * of the ip_shuffle ring (-32767 or swap with ourself), to avoid duplicates
       * occuring too quickly but also still be random.
       *
       * 0 is a special IP ID -- don't return it.
       */
      u_int16_t
      ip_randomid(void)
  227 {
              static int ipid_initialized;
              u_int16_t si, r;
              int i, i2;
      
  227         if (!ipid_initialized) {
                      ipid_initialized = 1;
      
                      /*
                       * Initialize with a random permutation. Do so using Knuth
                       * which avoids the exchange in the Durstenfeld shuffle.
                       * (See "The Art of Computer Programming, Vol 2" 3rd ed, pg. 145).
                       *
                       * Even if our PRNG is imperfect at boot time, we have deferred
                       * doing this until the first packet being sent and now must
                       * generate an ID.
                       */
                      for (i = 0; i < nitems(ip_shuffle); ++i) {
                              i2 = arc4random_uniform(i + 1);
                              ip_shuffle[i] = ip_shuffle[i2];
                              ip_shuffle[i2] = i;
                      }
              }
      
              do {
                      arc4random_buf(&si, sizeof(si));
                      i = isindex & 0xFFFF;
                      i2 = (isindex - (si & 0x7FFF)) & 0xFFFF;
                      r = ip_shuffle[i];
                      ip_shuffle[i] = ip_shuffle[i2];
                      ip_shuffle[i2] = r;
                      isindex++;
              } while (r == 0);
      
              return (r);
  227 }
      /*        $OpenBSD: sys_socket.c,v 1.42 2018/11/19 13:15:37 visa Exp $        */
      /*        $NetBSD: sys_socket.c,v 1.13 1995/08/12 23:59:09 mycroft Exp $        */
      
      /*
       * Copyright (c) 1982, 1986, 1990, 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.
       *
       *        @(#)sys_socket.c        8.1 (Berkeley) 6/10/93
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/file.h>
      #include <sys/proc.h>
      #include <sys/mbuf.h>
      #include <sys/protosw.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/ioctl.h>
      #include <sys/poll.h>
      #include <sys/stat.h>
      #include <sys/fcntl.h>
      
      #include <net/if.h>
      #include <net/route.h>
      
      struct        fileops socketops = {
              .fo_read        = soo_read,
              .fo_write        = soo_write,
              .fo_ioctl        = soo_ioctl,
              .fo_poll        = soo_poll,
              .fo_kqfilter        = soo_kqfilter,
              .fo_stat        = soo_stat,
              .fo_close        = soo_close
      };
      
      int
      soo_read(struct file *fp, struct uio *uio, int fflags)
   25 {
              struct socket *so = (struct socket *)fp->f_data;
              int flags = 0;
      
              if (fp->f_flag & FNONBLOCK)
                      flags |= MSG_DONTWAIT;
      
              return (soreceive(so, NULL, uio, NULL, NULL, &flags, 0));
      }
      
      int
      soo_write(struct file *fp, struct uio *uio, int fflags)
  204 {
              struct socket *so = (struct socket *)fp->f_data;
              int flags = 0;
      
              if (fp->f_flag & FNONBLOCK)
                      flags |= MSG_DONTWAIT;
      
              return (sosend(so, NULL, uio, NULL, NULL, flags));
      }
      
      int
      soo_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p)
  120 {
              struct socket *so = (struct socket *)fp->f_data;
              int s, error = 0;
      
   14         switch (cmd) {
      
              case FIONBIO:
                      break;
      
              case FIOASYNC:
                      s = solock(so);
                      if (*(int *)data) {
    4                         so->so_state |= SS_ASYNC;
                              so->so_rcv.sb_flags |= SB_ASYNC;
                              so->so_snd.sb_flags |= SB_ASYNC;
                      } else {
    2                         so->so_state &= ~SS_ASYNC;
                              so->so_rcv.sb_flags &= ~SB_ASYNC;
                              so->so_snd.sb_flags &= ~SB_ASYNC;
                      }
                      sounlock(so, s);
                      break;
      
              case FIONREAD:
    2                 *(int *)data = so->so_rcv.sb_datacc;
                      break;
      
              case TIOCSPGRP:
                      /* FALLTHROUGH */
              case SIOCSPGRP:
                      error = sigio_setown(&so->so_sigio, *(int *)data);
                      break;
      
              case TIOCGPGRP:
    2                 *(int *)data = -sigio_getown(&so->so_sigio);
                      break;
      
              case SIOCGPGRP:
    1                 *(int *)data = sigio_getown(&so->so_sigio);
                      break;
      
              case SIOCATMARK:
    2                 *(int *)data = (so->so_state&SS_RCVATMARK) != 0;
                      break;
      
              default:
                      /*
                       * Interface/routing/protocol specific ioctls:
                       * interface and routing ioctls should have a
                       * different entry since a socket's unnecessary
                       */
                      if (IOCGROUP(cmd) == 'i') {
   79                         error = ifioctl(so, cmd, data, p);
                              return (error);
                      }
                      if (IOCGROUP(cmd) == 'r')
                              return (EOPNOTSUPP);
   21                 error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
                          (struct mbuf *)cmd, (struct mbuf *)data, NULL, p));
                      break;
              }
      
              return (error);
      }
      
      int
      soo_poll(struct file *fp, int events, struct proc *p)
   38 {
              struct socket *so = fp->f_data;
              int revents = 0;
              int s;
      
              s = solock(so);
    9         if (events & (POLLIN | POLLRDNORM)) {
   34                 if (soreadable(so))
                              revents |= events & (POLLIN | POLLRDNORM);
              }
              /* NOTE: POLLHUP and POLLOUT/POLLWRNORM are mutually exclusive */
              if (so->so_state & SS_ISDISCONNECTED) {
    3                 revents |= POLLHUP;
   15         } else if (events & (POLLOUT | POLLWRNORM)) {
   29                 if (sowriteable(so))
                              revents |= events & (POLLOUT | POLLWRNORM);
              }
   22         if (events & (POLLPRI | POLLRDBAND)) {
   18                 if (so->so_oobmark || (so->so_state & SS_RCVATMARK))
                              revents |= events & (POLLPRI | POLLRDBAND);
              }
   30         if (revents == 0) {
    5                 if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
   16                         selrecord(p, &so->so_rcv.sb_sel);
                              so->so_rcv.sb_flags |= SB_SEL;
                      }
   13                 if (events & (POLLOUT | POLLWRNORM)) {
    9                         selrecord(p, &so->so_snd.sb_sel);
                              so->so_snd.sb_flags |= SB_SEL;
                      }
              }
              sounlock(so, s);
              return (revents);
      }
      
      int
      soo_stat(struct file *fp, struct stat *ub, struct proc *p)
      {
              struct socket *so = fp->f_data;
              int s;
      
              memset(ub, 0, sizeof (*ub));
              ub->st_mode = S_IFSOCK;
              s = solock(so);
              if ((so->so_state & SS_CANTRCVMORE) == 0 || so->so_rcv.sb_cc != 0)
                      ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
              if ((so->so_state & SS_CANTSENDMORE) == 0)
                      ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
              ub->st_uid = so->so_euid;
              ub->st_gid = so->so_egid;
              (void) ((*so->so_proto->pr_usrreq)(so, PRU_SENSE,
                  (struct mbuf *)ub, NULL, NULL, p));
              sounlock(so, s);
              return (0);
      }
      
      int
      soo_close(struct file *fp, struct proc *p)
   91 {
              int flags, error = 0;
      
              if (fp->f_data) {
   91                 flags = (fp->f_flag & FNONBLOCK) ? MSG_DONTWAIT : 0;
                      error = soclose(fp->f_data, flags);
              }
              fp->f_data = 0;
              return (error);
      }
      /*        $OpenBSD: virtio.c,v 1.19 2019/05/26 15:20:04 sf Exp $        */
      /*        $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $        */
      
      /*
       * Copyright (c) 2012 Stefan Fritsch, Alexander Fiveg.
       * Copyright (c) 2010 Minoura Makoto.
       * 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.
       *
       * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/kernel.h>
      #include <sys/device.h>
      #include <sys/mutex.h>
      #include <sys/atomic.h>
      #include <sys/malloc.h>
      
      #include <dev/pv/virtioreg.h>
      #include <dev/pv/virtiovar.h>
      
      #if VIRTIO_DEBUG
      #define VIRTIO_ASSERT(x)        KASSERT(x)
      #else
      #define VIRTIO_ASSERT(x)
      #endif
      
      void                 virtio_init_vq(struct virtio_softc *,
                                      struct virtqueue *);
      void                 vq_free_entry(struct virtqueue *, struct vq_entry *);
      struct vq_entry        *vq_alloc_entry(struct virtqueue *);
      
      struct cfdriver virtio_cd = {
              NULL, "virtio", DV_DULL
      };
      
      static const char * const virtio_device_name[] = {
              "Unknown (0)",                /* 0 */
              "Network",                /* 1 */
              "Block",                /* 2 */
              "Console",                /* 3 */
              "Entropy",                /* 4 */
              "Memory Balloon",        /* 5 */
              "IO Memory",                /* 6 */
              "Rpmsg",                /* 7 */
              "SCSI host",                /* 8 */
              "9P Transport",                /* 9 */
              "mac80211 wlan"                /* 10 */
      };
      #define NDEVNAMES        (sizeof(virtio_device_name)/sizeof(char*))
      
      const char *
      virtio_device_string(int id)
      {
              return id < NDEVNAMES ? virtio_device_name[id] : "Unknown";
      }
      
      #if VIRTIO_DEBUG
      static const struct virtio_feature_name transport_feature_names[] = {
              { VIRTIO_F_NOTIFY_ON_EMPTY,        "NotifyOnEmpty"},
              { VIRTIO_F_RING_INDIRECT_DESC,        "RingIndirectDesc"},
              { VIRTIO_F_RING_EVENT_IDX,        "RingEventIdx"},
              { VIRTIO_F_BAD_FEATURE,                "BadFeature"},
              { VIRTIO_F_VERSION_1,                "Version1"},
              { 0,                                NULL}
      };
      
      void
      virtio_log_features(uint64_t host, uint64_t neg,
          const struct virtio_feature_name *guest_feature_names)
      {
              const struct virtio_feature_name *namep;
              int i;
              char c;
              uint32_t bit;
      
              for (i = 0; i < 64; i++) {
                      if (i == 30) {
                              /*
                               * VIRTIO_F_BAD_FEATURE is only used for
                               * checking correct negotiation
                               */
                              continue;
                      }
                      bit = 1 << i;
                      if ((host&bit) == 0)
                              continue;
                      namep = (i < 24 || i > 37) ? guest_feature_names :
                          transport_feature_names;
                      while (namep->bit && namep->bit != bit)
                              namep++;
                      c = (neg&bit) ? '+' : '-';
                      if (namep->name)
                              printf(" %c%s", c, namep->name);
                      else
                              printf(" %cUnknown(%d)", c, i);
              }
      }
      #endif
      
      /*
       * Reset the device.
       */
      /*
       * To reset the device to a known state, do following:
       *        virtio_reset(sc);             // this will stop the device activity
       *        <dequeue finished requests>; // virtio_dequeue() still can be called
       *        <revoke pending requests in the vqs if any>;
       *        virtio_reinit_start(sc);     // dequeue prohibitted
       *        <some other initialization>;
       *        virtio_reinit_end(sc);             // device activated; enqueue allowed
       * Once attached, features are assumed to not change again.
       */
      void
      virtio_reset(struct virtio_softc *sc)
      {
              virtio_device_reset(sc);
              sc->sc_active_features = 0;
      }
      
      void
      virtio_reinit_start(struct virtio_softc *sc)
      {
              int i;
      
              virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
              virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
              virtio_negotiate_features(sc, NULL);
              for (i = 0; i < sc->sc_nvqs; i++) {
                      int n;
                      struct virtqueue *vq = &sc->sc_vqs[i];
                      n = virtio_read_queue_size(sc, vq->vq_index);
                      if (n == 0)        /* vq disappeared */
                              continue;
                      if (n != vq->vq_num) {
                              panic("%s: virtqueue size changed, vq index %d\n",
                                  sc->sc_dev.dv_xname, vq->vq_index);
                      }
                      virtio_init_vq(sc, vq);
                      virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr);
              }
      }
      
      void
      virtio_reinit_end(struct virtio_softc *sc)
      {
              virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK);
      }
      
      /*
       * dmamap sync operations for a virtqueue.
       */
      static inline void
      vq_sync_descs(struct virtio_softc *sc, struct virtqueue *vq, int ops)
      {
              /* availoffset == sizeof(vring_desc)*vq_num */
              bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 0, vq->vq_availoffset,
                  ops);
      }
      
      static inline void
      vq_sync_aring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
      {
              bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_availoffset,
                  offsetof(struct vring_avail, ring) + vq->vq_num * sizeof(uint16_t),
                  ops);
      }
      
      static inline void
      vq_sync_uring(struct virtio_softc *sc, struct virtqueue *vq, int ops)
      {
              bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_usedoffset,
                  offsetof(struct vring_used, ring) + vq->vq_num *
                  sizeof(struct vring_used_elem), ops);
      }
      
      static inline void
      vq_sync_indirect(struct virtio_softc *sc, struct virtqueue *vq, int slot,
          int ops)
      {
              int offset = vq->vq_indirectoffset +
                  sizeof(struct vring_desc) * vq->vq_maxnsegs * slot;
      
              bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, offset,
                  sizeof(struct vring_desc) * vq->vq_maxnsegs, ops);
      }
      
      /*
       * Scan vq, bus_dmamap_sync for the vqs (not for the payload),
       * and calls (*vq_done)() if some entries are consumed.
       * For use in transport specific irq handlers.
       */
      int
      virtio_check_vqs(struct virtio_softc *sc)
      {
              struct virtqueue *vq;
              int i, r = 0;
      
              /* going backwards is better for if_vio */
              for (i = sc->sc_nvqs - 1; i >= 0; i--) {
                      vq = &sc->sc_vqs[i];
                      if (vq->vq_queued) {
                              vq->vq_queued = 0;
                              vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE);
                      }
                      vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD);
                      if (vq->vq_used_idx != vq->vq_used->idx) {
                              if (vq->vq_done)
                                      r |= (vq->vq_done)(vq);
                      }
              }
      
              return r;
      }
      
      /*
       * Initialize vq structure.
       */
      void
      virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq)
      {
              int i, j;
              int vq_size = vq->vq_num;
      
              memset(vq->vq_vaddr, 0, vq->vq_bytesize);
      
              /* build the indirect descriptor chain */
              if (vq->vq_indirect != NULL) {
                      struct vring_desc *vd;
      
                      for (i = 0; i < vq_size; i++) {
                              vd = vq->vq_indirect;
                              vd += vq->vq_maxnsegs * i;
                              for (j = 0; j < vq->vq_maxnsegs-1; j++)
                                      vd[j].next = j + 1;
                      }
              }
      
              /* free slot management */
              SLIST_INIT(&vq->vq_freelist);
              /*
               * virtio_enqueue_trim needs monotonely raising entries, therefore
               * initialize in reverse order
               */
              for (i = vq_size - 1; i >= 0; i--) {
                      SLIST_INSERT_HEAD(&vq->vq_freelist, &vq->vq_entries[i],
                          qe_list);
                      vq->vq_entries[i].qe_index = i;
              }
      
              /* enqueue/dequeue status */
              vq->vq_avail_idx = 0;
              vq->vq_used_idx = 0;
              vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
              vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD);
              vq->vq_queued = 1;
      }
      
      /*
       * Allocate/free a vq.
       *
       * maxnsegs denotes how much space should be allocated for indirect
       * descriptors. maxnsegs == 1 can be used to disable use indirect
       * descriptors for this queue.
       */
      int
      virtio_alloc_vq(struct virtio_softc *sc, struct virtqueue *vq, int index,
          int maxsegsize, int maxnsegs, const char *name)
      {
              int vq_size, allocsize1, allocsize2, allocsize3, allocsize = 0;
              int rsegs, r, hdrlen;
      #define VIRTQUEUE_ALIGN(n)        (((n)+(VIRTIO_PAGE_SIZE-1))&        \
                                       ~(VIRTIO_PAGE_SIZE-1))
      
              memset(vq, 0, sizeof(*vq));
      
              vq_size = virtio_read_queue_size(sc, index);
              if (vq_size == 0) {
                      printf("virtqueue not exist, index %d for %s\n", index, name);
                      goto err;
              }
              if (((vq_size - 1) & vq_size) != 0)
                      panic("vq_size not power of two: %d", vq_size);
      
              hdrlen = virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX) ? 3 : 2;
      
              /* allocsize1: descriptor table + avail ring + pad */
              allocsize1 = VIRTQUEUE_ALIGN(sizeof(struct vring_desc) * vq_size
                  + sizeof(uint16_t) * (hdrlen + vq_size));
              /* allocsize2: used ring + pad */
              allocsize2 = VIRTQUEUE_ALIGN(sizeof(uint16_t) * hdrlen
                  + sizeof(struct vring_used_elem) * vq_size);
              /* allocsize3: indirect table */
              if (sc->sc_indirect && maxnsegs > 1)
                      allocsize3 = sizeof(struct vring_desc) * maxnsegs * vq_size;
              else
                      allocsize3 = 0;
              allocsize = allocsize1 + allocsize2 + allocsize3;
      
              /* alloc and map the memory */
              r = bus_dmamem_alloc(sc->sc_dmat, allocsize, VIRTIO_PAGE_SIZE, 0,
                  &vq->vq_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
              if (r != 0) {
                      printf("virtqueue %d for %s allocation failed, error %d\n",
                             index, name, r);
                      goto err;
              }
              r = bus_dmamem_map(sc->sc_dmat, &vq->vq_segs[0], 1, allocsize,
                  (caddr_t*)&vq->vq_vaddr, BUS_DMA_NOWAIT);
              if (r != 0) {
                      printf("virtqueue %d for %s map failed, error %d\n", index,
                          name, r);
                      goto err;
              }
              r = bus_dmamap_create(sc->sc_dmat, allocsize, 1, allocsize, 0,
                  BUS_DMA_NOWAIT, &vq->vq_dmamap);
              if (r != 0) {
                      printf("virtqueue %d for %s dmamap creation failed, "
                          "error %d\n", index, name, r);
                      goto err;
              }
              r = bus_dmamap_load(sc->sc_dmat, vq->vq_dmamap, vq->vq_vaddr,
                  allocsize, NULL, BUS_DMA_NOWAIT);
              if (r != 0) {
                      printf("virtqueue %d for %s dmamap load failed, error %d\n",
                          index, name, r);
                      goto err;
              }
      
              /* remember addresses and offsets for later use */
              vq->vq_owner = sc;
              vq->vq_num = vq_size;
              vq->vq_mask = vq_size - 1;
              vq->vq_index = index;
              vq->vq_desc = vq->vq_vaddr;
              vq->vq_availoffset = sizeof(struct vring_desc)*vq_size;
              vq->vq_avail = (struct vring_avail*)(((char*)vq->vq_desc) +
                  vq->vq_availoffset);
              vq->vq_usedoffset = allocsize1;
              vq->vq_used = (struct vring_used*)(((char*)vq->vq_desc) +
                  vq->vq_usedoffset);
              if (allocsize3 > 0) {
                      vq->vq_indirectoffset = allocsize1 + allocsize2;
                      vq->vq_indirect = (void*)(((char*)vq->vq_desc)
                          + vq->vq_indirectoffset);
              }
              vq->vq_bytesize = allocsize;
              vq->vq_maxnsegs = maxnsegs;
      
              /* free slot management */
              vq->vq_entries = mallocarray(vq_size, sizeof(struct vq_entry),
                  M_DEVBUF, M_NOWAIT | M_ZERO);
              if (vq->vq_entries == NULL) {
                      r = ENOMEM;
                      goto err;
              }
      
              virtio_init_vq(sc, vq);
              virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr);
      
      #if VIRTIO_DEBUG
              printf("\nallocated %u byte for virtqueue %d for %s, size %d\n",
                  allocsize, index, name, vq_size);
              if (allocsize3 > 0)
                      printf("using %d byte (%d entries) indirect descriptors\n",
                          allocsize3, maxnsegs * vq_size);
      #endif
              return 0;
      
      err:
              if (vq->vq_dmamap)
                      bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
              if (vq->vq_vaddr)
                      bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, allocsize);
              if (vq->vq_segs[0].ds_addr)
                      bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
              memset(vq, 0, sizeof(*vq));
      
              return -1;
      }
      
      int
      virtio_free_vq(struct virtio_softc *sc, struct virtqueue *vq)
      {
              struct vq_entry *qe;
              int i = 0;
      
              /* device must be already deactivated */
              /* confirm the vq is empty */
              SLIST_FOREACH(qe, &vq->vq_freelist, qe_list) {
                      i++;
              }
              if (i != vq->vq_num) {
                      printf("%s: freeing non-empty vq, index %d\n",
                          sc->sc_dev.dv_xname, vq->vq_index);
                      return EBUSY;
              }
      
              /* tell device that there's no virtqueue any longer */
              virtio_setup_queue(sc, vq, 0);
      
              free(vq->vq_entries, M_DEVBUF, 0);
              bus_dmamap_unload(sc->sc_dmat, vq->vq_dmamap);
              bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap);
              bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, vq->vq_bytesize);
              bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1);
              memset(vq, 0, sizeof(*vq));
      
              return 0;
      }
      
      /*
       * Free descriptor management.
       */
      struct vq_entry *
      vq_alloc_entry(struct virtqueue *vq)
      {
              struct vq_entry *qe;
      
              if (SLIST_EMPTY(&vq->vq_freelist))
                      return NULL;
              qe = SLIST_FIRST(&vq->vq_freelist);
              SLIST_REMOVE_HEAD(&vq->vq_freelist, qe_list);
      
              return qe;
      }
      
      void
      vq_free_entry(struct virtqueue *vq, struct vq_entry *qe)
      {
              SLIST_INSERT_HEAD(&vq->vq_freelist, qe, qe_list);
      }
      
      /*
       * Enqueue several dmamaps as a single request.
       */
      /*
       * Typical usage:
       *  <queue size> number of followings are stored in arrays
       *  - command blocks (in dmamem) should be pre-allocated and mapped
       *  - dmamaps for command blocks should be pre-allocated and loaded
       *  - dmamaps for payload should be pre-allocated
       *        r = virtio_enqueue_prep(sc, vq, &slot);                // allocate a slot
       *        if (r)                // currently 0 or EAGAIN
       *          return r;
       *        r = bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..);
       *        if (r) {
       *          virtio_enqueue_abort(sc, vq, slot);
       *          bus_dmamap_unload(dmat, dmamap_payload[slot]);
       *          return r;
       *        }
       *        r = virtio_enqueue_reserve(sc, vq, slot,
       *                                   dmamap_payload[slot]->dm_nsegs+1);
       *                                                        // ^ +1 for command
       *        if (r) {        // currently 0 or EAGAIN
       *          bus_dmamap_unload(dmat, dmamap_payload[slot]);
       *          return r;                                        // do not call abort()
       *        }
       *        <setup and prepare commands>
       *        bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE);
       *        bus_dmamap_sync(dmat, dmamap_payload[slot],...);
       *        virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0);
       *        virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite);
       *        virtio_enqueue_commit(sc, vq, slot, 1);
       *
       * Alternative usage with statically allocated slots:
       *        <during initialization>
       *        // while not out of slots, do
       *        virtio_enqueue_prep(sc, vq, &slot);                // allocate a slot
       *        virtio_enqueue_reserve(sc, vq, slot, max_segs);        // reserve all slots
       *                                                that may ever be needed
       *
       *        <when enqueing a request>
       *        // Don't call virtio_enqueue_prep()
       *        bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..);
       *        bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE);
       *        bus_dmamap_sync(dmat, dmamap_payload[slot],...);
       *        virtio_enqueue_trim(sc, vq, slot, num_segs_needed);
       *        virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0);
       *        virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite);
       *        virtio_enqueue_commit(sc, vq, slot, 1);
       *
       *        <when dequeuing>
       *        // don't call virtio_dequeue_commit()
       */
      
      /*
       * enqueue_prep: allocate a slot number
       */
      int
      virtio_enqueue_prep(struct virtqueue *vq, int *slotp)
      {
              struct vq_entry *qe1;
      
              VIRTIO_ASSERT(slotp != NULL);
      
              qe1 = vq_alloc_entry(vq);
              if (qe1 == NULL)
                      return EAGAIN;
              /* next slot is not allocated yet */
              qe1->qe_next = -1;
              *slotp = qe1->qe_index;
      
              return 0;
      }
      
      /*
       * enqueue_reserve: allocate remaining slots and build the descriptor chain.
       * Calls virtio_enqueue_abort() on failure.
       */
      int
      virtio_enqueue_reserve(struct virtqueue *vq, int slot, int nsegs)
      {
              struct vq_entry *qe1 = &vq->vq_entries[slot];
      
              VIRTIO_ASSERT(qe1->qe_next == -1);
              VIRTIO_ASSERT(1 <= nsegs && nsegs <= vq->vq_num);
      
              if (vq->vq_indirect != NULL && nsegs > 1 && nsegs <= vq->vq_maxnsegs) {
                      struct vring_desc *vd;
                      int i;
      
                      qe1->qe_indirect = 1;
      
                      vd = &vq->vq_desc[qe1->qe_index];
                      vd->addr = vq->vq_dmamap->dm_segs[0].ds_addr +
                          vq->vq_indirectoffset;
                      vd->addr += sizeof(struct vring_desc) * vq->vq_maxnsegs *
                          qe1->qe_index;
                      vd->len = sizeof(struct vring_desc) * nsegs;
                      vd->flags = VRING_DESC_F_INDIRECT;
      
                      vd = vq->vq_indirect;
                      vd += vq->vq_maxnsegs * qe1->qe_index;
                      qe1->qe_desc_base = vd;
      
                      for (i = 0; i < nsegs-1; i++)
                              vd[i].flags = VRING_DESC_F_NEXT;
                      vd[i].flags = 0;
                      qe1->qe_next = 0;
      
                      return 0;
              } else {
                      struct vring_desc *vd;
                      struct vq_entry *qe;
                      int i, s;
      
                      qe1->qe_indirect = 0;
      
                      vd = &vq->vq_desc[0];
                      qe1->qe_desc_base = vd;
                      qe1->qe_next = qe1->qe_index;
                      s = slot;
                      for (i = 0; i < nsegs - 1; i++) {
                              qe = vq_alloc_entry(vq);
                              if (qe == NULL) {
                                      vd[s].flags = 0;
                                      virtio_enqueue_abort(vq, slot);
                                      return EAGAIN;
                              }
                              vd[s].flags = VRING_DESC_F_NEXT;
                              vd[s].next = qe->qe_index;
                              s = qe->qe_index;
                      }
                      vd[s].flags = 0;
      
                      return 0;
              }
      }
      
      /*
       * enqueue: enqueue a single dmamap.
       */
      int
      virtio_enqueue(struct virtqueue *vq, int slot, bus_dmamap_t dmamap, int write)
  252 {
              struct vq_entry *qe1 = &vq->vq_entries[slot];
              struct vring_desc *vd = qe1->qe_desc_base;
              int i;
              int s = qe1->qe_next;
      
              VIRTIO_ASSERT(s >= 0);
              VIRTIO_ASSERT(dmamap->dm_nsegs > 0);
              if (dmamap->dm_nsegs > vq->vq_maxnsegs) {
      #if VIRTIO_DEBUG
                      for (i = 0; i < dmamap->dm_nsegs; i++) {
                              printf(" %d (%d): %p %lx \n", i, write,
                                  (void *)dmamap->dm_segs[i].ds_addr,
                                  dmamap->dm_segs[i].ds_len);
                      }
      #endif
                      panic("dmamap->dm_nseg %d > vq->vq_maxnsegs %d\n",
                          dmamap->dm_nsegs, vq->vq_maxnsegs);
              }
      
  252         for (i = 0; i < dmamap->dm_nsegs; i++) {
                      vd[s].addr = dmamap->dm_segs[i].ds_addr;
                      vd[s].len = dmamap->dm_segs[i].ds_len;
  247                 if (!write)
   12                         vd[s].flags |= VRING_DESC_F_WRITE;
                      s = vd[s].next;
              }
              qe1->qe_next = s;
      
              return 0;
      }
      
      int
      virtio_enqueue_p(struct virtqueue *vq, int slot, bus_dmamap_t dmamap,
          bus_addr_t start, bus_size_t len, int write)
  254 {
              struct vq_entry *qe1 = &vq->vq_entries[slot];
              struct vring_desc *vd = qe1->qe_desc_base;
              int s = qe1->qe_next;
      
              VIRTIO_ASSERT(s >= 0);
              /* XXX todo: handle more segments */
              VIRTIO_ASSERT(dmamap->dm_nsegs == 1);
              VIRTIO_ASSERT((dmamap->dm_segs[0].ds_len > start) &&
                  (dmamap->dm_segs[0].ds_len >= start + len));
      
              vd[s].addr = dmamap->dm_segs[0].ds_addr + start;
              vd[s].len = len;
  254         if (!write)
  254                 vd[s].flags |= VRING_DESC_F_WRITE;
              qe1->qe_next = vd[s].next;
      
              return 0;
      }
      
      static void
      publish_avail_idx(struct virtio_softc *sc, struct virtqueue *vq)
      {
              vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
      
              virtio_membar_producer();
              vq->vq_avail->idx = vq->vq_avail_idx;
              vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE);
              vq->vq_queued = 1;
      }
      
      /*
       * enqueue_commit: add it to the aring.
       */
      void
      virtio_enqueue_commit(struct virtio_softc *sc, struct virtqueue *vq, int slot,
          int notifynow)
  254 {
              struct vq_entry *qe1;
      
              if (slot < 0)
                      goto notify;
              vq_sync_descs(sc, vq, BUS_DMASYNC_PREWRITE);
              qe1 = &vq->vq_entries[slot];
  254         if (qe1->qe_indirect)
                      vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_PREWRITE);
              vq->vq_avail->ring[(vq->vq_avail_idx++) & vq->vq_mask] = slot;
      
      notify:
              if (notifynow) {
                      if (virtio_has_feature(vq->vq_owner, VIRTIO_F_RING_EVENT_IDX)) {
                              uint16_t o = vq->vq_avail->idx;
                              uint16_t n = vq->vq_avail_idx;
                              uint16_t t;
                              publish_avail_idx(sc, vq);
      
                              virtio_membar_sync();
                              t = VQ_AVAIL_EVENT(vq) + 1;
                              if ((uint16_t)(n - t) < (uint16_t)(n - o))
                                      sc->sc_ops->kick(sc, vq->vq_index);
                      } else {
                              publish_avail_idx(sc, vq);
      
                              virtio_membar_sync();
   24                         if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY))
  254                                 sc->sc_ops->kick(sc, vq->vq_index);
                      }
              }
      }
      
      /*
       * enqueue_abort: rollback.
       */
      int
      virtio_enqueue_abort(struct virtqueue *vq, int slot)
      {
              struct vq_entry *qe = &vq->vq_entries[slot];
              struct vring_desc *vd;
              int s;
      
              if (qe->qe_next < 0) {
                      vq_free_entry(vq, qe);
                      return 0;
              }
      
              s = slot;
              vd = &vq->vq_desc[0];
              while (vd[s].flags & VRING_DESC_F_NEXT) {
                      s = vd[s].next;
                      vq_free_entry(vq, qe);
                      qe = &vq->vq_entries[s];
              }
              vq_free_entry(vq, qe);
              return 0;
      }
      
      /*
       * enqueue_trim: adjust buffer size to given # of segments, a.k.a.
       * descriptors.
       */
      void
      virtio_enqueue_trim(struct virtqueue *vq, int slot, int nsegs)
  254 {
              struct vq_entry *qe1 = &vq->vq_entries[slot];
              struct vring_desc *vd = &vq->vq_desc[0];
              int i;
      
              if ((vd[slot].flags & VRING_DESC_F_INDIRECT) == 0) {
  254                 qe1->qe_next = qe1->qe_index;
                      /*
                       * N.B.: the vq_entries are ASSUMED to be a contiguous
                       *       block with slot being the index to the first one.
                       */
              } else {
                      qe1->qe_next = 0;
                      vd = &vq->vq_desc[qe1->qe_index];
                      vd->len = sizeof(struct vring_desc) * nsegs;
                      vd = qe1->qe_desc_base;
                      slot = 0;
              }
      
  254         for (i = 0; i < nsegs -1 ; i++) {
  247                 vd[slot].flags = VRING_DESC_F_NEXT;
                      slot++;
              }
              vd[slot].flags = 0;
      }
      
      /*
       * Dequeue a request.
       */
      /*
       * dequeue: dequeue a request from uring; dmamap_sync for uring is
       *            already done in the interrupt handler.
       */
      int
      virtio_dequeue(struct virtio_softc *sc, struct virtqueue *vq,
          int *slotp, int *lenp)
      {
              uint16_t slot, usedidx;
              struct vq_entry *qe;
      
              if (vq->vq_used_idx == vq->vq_used->idx)
                      return ENOENT;
              usedidx = vq->vq_used_idx++;
              usedidx &= vq->vq_mask;
      
              virtio_membar_consumer();
              slot = vq->vq_used->ring[usedidx].id;
              qe = &vq->vq_entries[slot];
      
              if (qe->qe_indirect)
                      vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_POSTWRITE);
      
              if (slotp)
                      *slotp = slot;
              if (lenp)
                      *lenp = vq->vq_used->ring[usedidx].len;
      
              return 0;
      }
      
      /*
       * dequeue_commit: complete dequeue; the slot is recycled for future use.
       *                 if you forget to call this the slot will be leaked.
       *
       *                 Don't call this if you use statically allocated slots
       *                 and virtio_dequeue_trim().
       */
      int
      virtio_dequeue_commit(struct virtqueue *vq, int slot)
      {
              struct vq_entry *qe = &vq->vq_entries[slot];
              struct vring_desc *vd = &vq->vq_desc[0];
              int s = slot;
      
              while (vd[s].flags & VRING_DESC_F_NEXT) {
                      s = vd[s].next;
                      vq_free_entry(vq, qe);
                      qe = &vq->vq_entries[s];
              }
              vq_free_entry(vq, qe);
      
              return 0;
      }
      
      /*
       * Increase the event index in order to delay interrupts.
       * Returns 0 on success; returns 1 if the used ring has already advanced
       * too far, and the caller must process the queue again (otherewise, no
       * more interrupts will happen).
       */
      int
      virtio_postpone_intr(struct virtqueue *vq, uint16_t nslots)
      {
              uint16_t        idx;
      
              idx = vq->vq_used_idx + nslots;
      
              /* set the new event index: avail_ring->used_event = idx */
              VQ_USED_EVENT(vq) = idx;
              virtio_membar_sync();
      
              vq_sync_aring(vq->vq_owner, vq, BUS_DMASYNC_PREWRITE);
              vq->vq_queued++;
      
              if (nslots < virtio_nused(vq))
                      return 1;
      
              return 0;
      }
      
      /*
       * Postpone interrupt until 3/4 of the available descriptors have been
       * consumed.
       */
      int
      virtio_postpone_intr_smart(struct virtqueue *vq)
      {
              uint16_t        nslots;
      
              nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx) * 3 / 4;
      
              return virtio_postpone_intr(vq, nslots);
      }
      
      /*
       * Postpone interrupt until all of the available descriptors have been
       * consumed.
       */
      int
      virtio_postpone_intr_far(struct virtqueue *vq)
      {
              uint16_t        nslots;
      
              nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx);
      
              return virtio_postpone_intr(vq, nslots);
      }
      
      
      /*
       * Start/stop vq interrupt.  No guarantee.
       */
      void
      virtio_stop_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
      {
              if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX)) {
                      /*
                       * No way to disable the interrupt completely with
                       * RingEventIdx. Instead advance used_event by half
                       * the possible value. This won't happen soon and
                       * is far enough in the past to not trigger a spurios
                       * interrupt.
                       */
                      VQ_USED_EVENT(vq) = vq->vq_used_idx + 0x8000;
              } else {
                      vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
              }
              vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
              vq->vq_queued++;
      }
      
      int
      virtio_start_vq_intr(struct virtio_softc *sc, struct virtqueue *vq)
      {
              /*
               * If event index feature is negotiated, enabling
               * interrupts is done through setting the latest
               * consumed index in the used_event field
               */
              if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX))
                      VQ_USED_EVENT(vq) = vq->vq_used_idx;
              else
                      vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
      
              virtio_membar_sync();
      
              vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
              vq->vq_queued++;
      
              if (vq->vq_used_idx != vq->vq_used->idx)
                      return 1;
      
              return 0;
      }
      
      /*
       * Returns a number of slots in the used ring available to
       * be supplied to the avail ring.
       */
      int
      virtio_nused(struct virtqueue *vq)
      {
              uint16_t        n;
      
              n = (uint16_t)(vq->vq_used->idx - vq->vq_used_idx);
              VIRTIO_ASSERT(n <= vq->vq_num);
      
              return n;
      }
      
      #if VIRTIO_DEBUG
      void
      virtio_vq_dump(struct virtqueue *vq)
      {
              /* Common fields */
              printf(" + vq num: %d\n", vq->vq_num);
              printf(" + vq mask: 0x%X\n", vq->vq_mask);
              printf(" + vq index: %d\n", vq->vq_index);
              printf(" + vq used idx: %d\n", vq->vq_used_idx);
              printf(" + vq avail idx: %d\n", vq->vq_avail_idx);
              printf(" + vq queued: %d\n",vq->vq_queued);
              /* Avail ring fields */
              printf(" + avail flags: 0x%X\n", vq->vq_avail->flags);
              printf(" + avail idx: %d\n", vq->vq_avail->idx);
              printf(" + avail event: %d\n", VQ_AVAIL_EVENT(vq));
              /* Used ring fields */
              printf(" + used flags: 0x%X\n",vq->vq_used->flags);
              printf(" + used idx: %d\n",vq->vq_used->idx);
              printf(" + used event: %d\n", VQ_USED_EVENT(vq));
              printf(" +++++++++++++++++++++++++++\n");
      }
      #endif
      /*        $OpenBSD: ip6_id.c,v 1.13 2017/09/08 05:36:53 deraadt Exp $        */
      /*        $NetBSD: ip6_id.c,v 1.7 2003/09/13 21:32:59 itojun Exp $        */
      /*        $KAME: ip6_id.c,v 1.8 2003/09/06 13:41:06 itojun Exp $        */
      
      /*
       * Copyright (C) 2003 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 1998 Niels Provos <provos@citi.umich.edu>
       * All rights reserved.
       *
       * Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
       * such a mathematical system to generate more random (yet non-repeating)
       * ids to solve the resolver/named problem.  But Niels designed the
       * actual system based on the constraints.
       *
       * 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.
       *
       * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
       */
      
      /*
       * seed = random (bits - 1) bit
       * n = prime, g0 = generator to n,
       * j = random so that gcd(j,n-1) == 1
       * g = g0^j mod n will be a generator again.
       *
       * X[0] = random seed.
       * X[n] = a*X[n-1]+b mod m is a Linear Congruential Generator
       * with a = 7^(even random) mod m,
       *      b = random with gcd(b,m) == 1
       *      m = constant and a maximal period of m-1.
       *
       * The transaction id is determined by:
       * id[n] = seed xor (g^X[n] mod n)
       *
       * Effectivly the id is restricted to the lower (bits - 1) bits, thus
       * yielding two different cycles by toggling the msb on and off.
       * This avoids reuse issues caused by reseeding.
       */
      
      #include <sys/param.h>
      #include <sys/kernel.h>
      #include <sys/socket.h>
      #include <sys/systm.h>
      
      #include <netinet/in.h>
      #include <netinet/ip6.h>
      #include <netinet6/ip6_var.h>
      
      struct randomtab {
              const int        ru_bits; /* resulting bits */
              const long        ru_out;        /* Time after wich will be reseeded */
              const u_int32_t ru_max;        /* Uniq cycle, avoid blackjack prediction */
              const u_int32_t ru_gen;        /* Starting generator */
              const u_int32_t ru_n;        /* ru_n: prime, ru_n - 1: product of pfacts[] */
              const u_int32_t ru_agen; /* determine ru_a as ru_agen^(2*rand) */
              const u_int32_t ru_m;        /* ru_m = 2^x*3^y */
              const u_int32_t pfacts[4];        /* factors of ru_n */
      
              u_int32_t ru_counter;
              u_int32_t ru_msb;
      
              u_int32_t ru_x;
              u_int32_t ru_seed, ru_seed2;
              u_int32_t ru_a, ru_b;
              u_int32_t ru_g;
              long ru_reseed;
      };
      
      static struct randomtab randomtab_20 = {
              20,                        /* resulting bits */
              180,                        /* Time after wich will be reseeded */
              200000,                        /* Uniq cycle, avoid blackjack prediction */
              2,                        /* Starting generator */
              524269,                        /* RU_N-1 = 2^2*3^2*14563 */
              7,                        /* determine ru_a as RU_AGEN^(2*rand) */
              279936,                        /* RU_M = 2^7*3^7 - don't change */
              { 2, 3, 14563, 0 },        /* factors of ru_n */
      };
      
      u_int32_t ip6id_pmod(u_int32_t, u_int32_t, u_int32_t);
      void ip6id_initid(struct randomtab *);
      u_int32_t ip6id_randomid(struct randomtab *);
      
      /*
       * Do a fast modular exponation, returned value will be in the range
       * of 0 - (mod-1)
       */
      
      u_int32_t
      ip6id_pmod(u_int32_t gen, u_int32_t expo, u_int32_t mod)
      {
              u_int64_t s, t, u;
      
              s = 1;
              t = gen;
              u = expo;
      
   81         while (u) {
   81                 if (u & 1)
   81                         s = (s * t) % mod;
                      u >>= 1;
                      t = (t * t) % mod;
              }
              return (s);
      }
      
      /*
       * Initializes the seed and chooses a suitable generator. Also toggles
       * the msb flag. The msb flag is used to generate two distinct
       * cycles of random numbers and thus avoiding reuse of ids.
       *
       * This function is called from id_randomid() when needed, an
       * application does not have to worry about it.
       */
      void
      ip6id_initid(struct randomtab *p)
      {
              u_int32_t j, i;
              int noprime = 1;
      
              p->ru_x = arc4random_uniform(p->ru_m);
      
              /* (bits - 1) bits of random seed */
              p->ru_seed = arc4random() & (~0U >> (32 - p->ru_bits + 1));
              p->ru_seed2 = arc4random() & (~0U >> (32 - p->ru_bits + 1));
      
              /* Determine the LCG we use */
              p->ru_b = (arc4random() & (~0U >> (32 - p->ru_bits))) | 1;
              p->ru_a = ip6id_pmod(p->ru_agen,
                  (arc4random() & (~0U >> (32 - p->ru_bits))) & (~1U), p->ru_m);
              while (p->ru_b % 3 == 0)
                      p->ru_b += 2;
      
              j = arc4random_uniform(p->ru_n);
      
              /*
               * Do a fast gcd(j, RU_N - 1), so we can find a j with
               * gcd(j, RU_N - 1) == 1, giving a new generator for
               * RU_GEN^j mod RU_N
               */
              while (noprime) {
                      for (i = 0; p->pfacts[i] > 0; i++)
                              if (j % p->pfacts[i] == 0)
                                      break;
      
                      if (p->pfacts[i] == 0)
                              noprime = 0;
                      else
                              j = (j + 1) % p->ru_n;
              }
      
              p->ru_g = ip6id_pmod(p->ru_gen, j, p->ru_n);
              p->ru_counter = 0;
      
              p->ru_reseed = time_uptime + p->ru_out;
              p->ru_msb = p->ru_msb ? 0 : (1U << (p->ru_bits - 1));
      }
      
      u_int32_t
      ip6id_randomid(struct randomtab *p)
   81 {
   50         int i, n;
      
   81         if (p->ru_counter >= p->ru_max || time_uptime > p->ru_reseed)
                      ip6id_initid(p);
      
              /* Skip a random number of ids */
              n = arc4random() & 0x3;
   81         if (p->ru_counter + n >= p->ru_max)
                      ip6id_initid(p);
      
   81         for (i = 0; i <= n; i++) {
                      /* Linear Congruential Generator */
                      p->ru_x = (u_int32_t)((u_int64_t)p->ru_a * p->ru_x + p->ru_b) % p->ru_m;
              }
      
              p->ru_counter += i;
      
   81         return (p->ru_seed ^ ip6id_pmod(p->ru_g, p->ru_seed2 + p->ru_x, p->ru_n)) |
                  p->ru_msb;
      }
      
      u_int32_t
      ip6_randomflowlabel(void)
   81 {
              return ip6id_randomid(&randomtab_20) & 0xfffff;
      }
      
      /*        $OpenBSD: syscall_mi.h,v 1.21 2019/06/14 05:52:42 deraadt Exp $        */
      
      /*
       * Copyright (c) 1982, 1986, 1989, 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.
       *
       *        @(#)kern_xxx.c        8.2 (Berkeley) 11/14/93
       */
      
      #include <sys/param.h>
      #include <sys/pledge.h>
      #include <uvm/uvm_extern.h>
      
      #ifdef KTRACE
      #include <sys/ktrace.h>
      #endif
      
      
      /*
       * The MD setup for a system call has been done; here's the MI part.
       */
      static inline int
      mi_syscall(struct proc *p, register_t code, const struct sysent *callp,
          register_t *argp, register_t retval[2])
      {
              uint64_t tval;
              int lock = !(callp->sy_flags & SY_NOLOCK);
              int error, pledged;
      
              /* refresh the thread's cache of the process's creds */
 7685         refreshcreds(p);
      
      #ifdef SYSCALL_DEBUG
              KERNEL_LOCK();
              scdebug_call(p, code, argp);
              KERNEL_UNLOCK();
      #endif
      #ifdef KTRACE
 7666         if (KTRPOINT(p, KTR_SYSCALL)) {
   25                 KERNEL_LOCK();
                      ktrsyscall(p, code, callp->sy_argsize, argp);
                      KERNEL_UNLOCK();
              }
      #endif
      
              /* SP must be within MAP_STACK space */
              if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p),
                  "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n",
                  uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial))
                      return (EPERM);
      
              /* PC must not be in writeable memory */
              if (!uvm_map_inentry(p, &p->p_pcinentry, PROC_PC(p),
                  "[%s]%d/%d pc=%lx inside %lx-%lx: writeable syscall\n",
                  uvm_map_inentry_pc, p->p_vmspace->vm_map.wserial))
                      return (EPERM);
      
 3827         if (lock)
 4330                 KERNEL_LOCK();
              pledged = (p->p_p->ps_flags & PS_PLEDGE);
 7685         if (pledged && (error = pledge_syscall(p, code, &tval))) {
                      if (!lock)
                              KERNEL_LOCK();
                      error = pledge_fail(p, error, tval);
                      KERNEL_UNLOCK();
                      return (error);
              }
              error = (*callp->sy_call)(p, argp, retval);
 3786         if (lock)
 4283                 KERNEL_UNLOCK();
      
              return (error);
      }
      
      /*
       * Finish MI stuff on return, after the registers have been set
       */
      static inline void
      mi_syscall_return(struct proc *p, register_t code, int error,
          const register_t retval[2])
      {
      #ifdef SYSCALL_DEBUG
              KERNEL_LOCK();
              scdebug_ret(p, code, error, retval);
              KERNEL_UNLOCK();
      #endif
      
              userret(p);
      
      #ifdef KTRACE
 7593         if (KTRPOINT(p, KTR_SYSRET)) {
   54                 KERNEL_LOCK();
                      ktrsysret(p, code, error, retval);
                      KERNEL_UNLOCK();
              }
      #endif
      }
      
      /*
       * Finish MI stuff for a new process/thread to return
       */
      static inline void
      mi_child_return(struct proc *p)
      {
      #if defined(SYSCALL_DEBUG) || defined(KTRACE)
              int code = (p->p_flag & P_THREAD) ? SYS___tfork :
                  (p->p_p->ps_flags & PS_PPWAIT) ? SYS_vfork : SYS_fork;
              const register_t child_retval[2] = { 0, 1 };
      #endif
      
      #ifdef SYSCALL_DEBUG
              KERNEL_LOCK();
              scdebug_ret(p, code, 0, child_retval);
              KERNEL_UNLOCK();
      #endif
      
              userret(p);
      
      #ifdef KTRACE
              if (KTRPOINT(p, KTR_SYSRET)) {
                      KERNEL_LOCK();
                      ktrsysret(p, code, 0, child_retval);
                      KERNEL_UNLOCK();
              }
      #endif
      }
      
      /* 
       * Do the specific processing necessary for an AST
       */
      static inline void
      mi_ast(struct proc *p, int resched)
      {
 2513         if (p->p_flag & P_OWEUPC) {
                      KERNEL_LOCK();
                      ADDUPROF(p);
                      KERNEL_UNLOCK();
              }
  170         if (resched)
 2379                 preempt();
      
              /*
               * XXX could move call to userret() here, but
               * hppa calls ast() in syscall return and sh calls
               * it after userret()
               */
      }
      /*        $OpenBSD: ip_mroute.c,v 1.127 2019/06/21 17:11:42 mpi Exp $        */
      /*        $NetBSD: ip_mroute.c,v 1.85 2004/04/26 01:31:57 matt Exp $        */
      
      /*
       * Copyright (c) 1989 Stephen Deering
       * Copyright (c) 1992, 1993
       *      The Regents of the University of California.  All rights reserved.
       *
       * This code is derived from software contributed to Berkeley by
       * Stephen Deering of Stanford University.
       *
       * 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.
       *
       *      @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93
       */
      
      /*
       * IP multicast forwarding procedures
       *
       * Written by David Waitzman, BBN Labs, August 1988.
       * Modified by Steve Deering, Stanford, February 1989.
       * Modified by Mark J. Steiglitz, Stanford, May, 1991
       * Modified by Van Jacobson, LBL, January 1993
       * Modified by Ajit Thyagarajan, PARC, August 1993
       * Modified by Bill Fenner, PARC, April 1994
       * Modified by Charles M. Hannum, NetBSD, May 1995.
       * Modified by Ahmed Helmy, SGI, June 1996
       * Modified by George Edmond Eddy (Rusty), ISI, February 1998
       * Modified by Pavlin Radoslavov, USC/ISI, May 1998, August 1999, October 2000
       * Modified by Hitoshi Asaeda, WIDE, August 2000
       * Modified by Pavlin Radoslavov, ICSI, October 2002
       *
       * MROUTING Revision: 1.2
       * advanced API support, bandwidth metering and signaling
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/mbuf.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/protosw.h>
      #include <sys/ioctl.h>
      #include <sys/syslog.h>
      
      #include <net/if.h>
      #include <net/if_var.h>
      #include <net/route.h>
      
      #include <netinet/in.h>
      #include <netinet/ip.h>
      #include <netinet/ip_var.h>
      #include <netinet/in_pcb.h>
      #include <netinet/igmp.h>
      #include <netinet/ip_mroute.h>
      
      /* #define MCAST_DEBUG */
      
      #ifdef MCAST_DEBUG
      int mcast_debug = 1;
      #define DPRINTF(fmt, args...)                                                \
              do {                                                                \
                      if (mcast_debug)                                        \
                              printf("%s:%d " fmt "\n",                        \
                                  __func__, __LINE__, ## args);                \
              } while (0)
      #else
      #define DPRINTF(fmt, args...)                        \
              do { } while (0)
      #endif
      
      /*
       * Globals.  All but ip_mrouter and ip_mrtproto could be static,
       * except for netstat or debugging purposes.
       */
      struct socket        *ip_mrouter[RT_TABLEID_MAX + 1];
      struct rttimer_queue *mrouterq[RT_TABLEID_MAX + 1];
      uint64_t         mrt_count[RT_TABLEID_MAX + 1];
      int                ip_mrtproto = IGMP_DVMRP;    /* for netstat only */
      
      struct mrtstat        mrtstat;
      
      struct rtentry        *mfc_find(struct ifnet *, struct in_addr *,
          struct in_addr *, unsigned int);
      int get_sg_cnt(unsigned int, struct sioc_sg_req *);
      int get_vif_cnt(unsigned int, struct sioc_vif_req *);
      int mrt_rtwalk_mfcsysctl(struct rtentry *, void *, unsigned int);
      int ip_mrouter_init(struct socket *, struct mbuf *);
      int mrouter_rtwalk_delete(struct rtentry *, void *, unsigned int);
      int get_version(struct mbuf *);
      int add_vif(struct socket *, struct mbuf *);
      int del_vif(struct socket *, struct mbuf *);
      void update_mfc_params(struct mfcctl2 *, int, unsigned int);
      void mfc_expire_route(struct rtentry *, struct rttimer *);
      int mfc_add(struct mfcctl2 *, struct in_addr *, struct in_addr *,
          int, unsigned int, int);
      int add_mfc(struct socket *, struct mbuf *);
      int del_mfc(struct socket *, struct mbuf *);
      int set_api_config(struct socket *, struct mbuf *); /* chose API capabilities */
      int get_api_support(struct mbuf *);
      int get_api_config(struct mbuf *);
      int socket_send(struct socket *, struct mbuf *,
                                  struct sockaddr_in *);
      int ip_mdq(struct mbuf *, struct ifnet *, struct rtentry *);
      struct ifnet *if_lookupbyvif(vifi_t, unsigned int);
      struct rtentry *rt_mcast_add(struct ifnet *, struct sockaddr *,
          struct sockaddr *);
      int rt_mcast_del(struct rtentry *, unsigned int);
      
      /*
       * Kernel multicast routing API capabilities and setup.
       * If more API capabilities are added to the kernel, they should be
       * recorded in `mrt_api_support'.
       */
      static const u_int32_t mrt_api_support = (MRT_MFC_FLAGS_DISABLE_WRONGVIF |
                                                MRT_MFC_RP);
      static u_int32_t mrt_api_config = 0;
      
      /*
       * Find a route for a given origin IP address and Multicast group address
       * Type of service parameter to be added in the future!!!
       * Statistics are updated by the caller if needed
       * (mrtstat.mrts_mfc_lookups and mrtstat.mrts_mfc_misses)
       */
      struct rtentry *
      mfc_find(struct ifnet *ifp, struct in_addr *origin, struct in_addr *group,
          unsigned int rtableid)
      {
              struct rtentry                *rt;
              struct sockaddr_in         msin;
      
              memset(&msin, 0, sizeof(msin));
              msin.sin_len = sizeof(msin);
              msin.sin_family = AF_INET;
              msin.sin_addr = *group;
      
              rt = rtalloc(sintosa(&msin), 0, rtableid);
              do {
                      if (!rtisvalid(rt)) {
                              rtfree(rt);
                              return NULL;
                      }
                      /* Don't consider non multicast routes. */
                      if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
                          (RTF_HOST | RTF_MULTICAST))
                              continue;
                      /* Return first occurrence if interface is not specified. */
                      if (ifp == NULL)
                              return (rt);
                      if (rt->rt_ifidx == ifp->if_index)
                              return (rt);
              } while ((rt = rtable_iterate(rt)) != NULL);
      
              return (NULL);
      }
      
      /*
       * Handle MRT setsockopt commands to modify the multicast routing tables.
       */
      int
      ip_mrouter_set(struct socket *so, int optname, struct mbuf *m)
   11 {
              struct inpcb *inp = sotoinpcb(so);
              int error;
      
    9         if (optname != MRT_INIT &&
                  so != ip_mrouter[inp->inp_rtableid])
                      error = ENOPROTOOPT;
              else
                      switch (optname) {
                      case MRT_INIT:
    2                         error = ip_mrouter_init(so, m);
                              break;
                      case MRT_DONE:
                              error = ip_mrouter_done(so);
                              break;
                      case MRT_ADD_VIF:
                              error = add_vif(so, m);
                              break;
                      case MRT_DEL_VIF:
                              error = del_vif(so, m);
                              break;
                      case MRT_ADD_MFC:
                              error = add_mfc(so, m);
                              break;
                      case MRT_DEL_MFC:
                              error = del_mfc(so, m);
                              break;
                      case MRT_API_CONFIG:
                              error = set_api_config(so, m);
                              break;
                      default:
                              error = ENOPROTOOPT;
                              break;
                      }
      
              return (error);
      }
      
      /*
       * Handle MRT getsockopt commands
       */
      int
      ip_mrouter_get(struct socket *so, int optname, struct mbuf *m)
   10 {
              struct inpcb *inp = sotoinpcb(so);
              int error;
      
   10         if (so != ip_mrouter[inp->inp_rtableid])
                      error = ENOPROTOOPT;
              else {
                      switch (optname) {
                      case MRT_VERSION:
                              error = get_version(m);
                              break;
                      case MRT_API_SUPPORT:
                              error = get_api_support(m);
                              break;
                      case MRT_API_CONFIG:
                              error = get_api_config(m);
                              break;
                      default:
                              error = ENOPROTOOPT;
                              break;
                      }
              }
      
              return (error);
      }
      
      /*
       * Handle ioctl commands to obtain information from the cache
       */
      int
      mrt_ioctl(struct socket *so, u_long cmd, caddr_t data)
    2 {
              struct inpcb *inp = sotoinpcb(so);
              int error;
      
    1         if (inp == NULL)
                      return (ENOTCONN);
      
    1         if (so != ip_mrouter[inp->inp_rtableid])
                      error = EINVAL;
              else
                      switch (cmd) {
                      case SIOCGETVIFCNT:
                              NET_RLOCK();
                              error = get_vif_cnt(inp->inp_rtableid,
                                  (struct sioc_vif_req *)data);
                              NET_RUNLOCK();
                              break;
                      case SIOCGETSGCNT:
                              NET_RLOCK();
                              error = get_sg_cnt(inp->inp_rtableid,
                                  (struct sioc_sg_req *)data);
                              NET_RUNLOCK();
                              break;
                      default:
                              error = ENOTTY;
                              break;
                      }
      
              return (error);
      }
      
      /*
       * returns the packet, byte, rpf-failure count for the source group provided
       */
      int
      get_sg_cnt(unsigned int rtableid, struct sioc_sg_req *req)
      {
              struct rtentry *rt;
              struct mfc *mfc;
      
              rt = mfc_find(NULL, &req->src, &req->grp, rtableid);
              if (rt == NULL) {
                      req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff;
                      return (EADDRNOTAVAIL);
              }
      
              req->pktcnt = req->bytecnt = req->wrong_if = 0;
              do {
                      /* Don't consider non multicast routes. */
                      if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
                          (RTF_HOST | RTF_MULTICAST))
                              continue;
      
                      mfc = (struct mfc *)rt->rt_llinfo;
                      if (mfc == NULL)
                              continue;
      
                      req->pktcnt += mfc->mfc_pkt_cnt;
                      req->bytecnt += mfc->mfc_byte_cnt;
                      req->wrong_if += mfc->mfc_wrong_if;
              } while ((rt = rtable_iterate(rt)) != NULL);
      
              return (0);
      }
      
      /*
       * returns the input and output packet and byte counts on the vif provided
       */
      int
      get_vif_cnt(unsigned int rtableid, struct sioc_vif_req *req)
      {
              struct ifnet        *ifp;
              struct vif        *v;
              vifi_t                 vifi = req->vifi;
      
              if ((ifp = if_lookupbyvif(vifi, rtableid)) == NULL)
                      return (EINVAL);
      
              v = (struct vif *)ifp->if_mcast;
              req->icount = v->v_pkt_in;
              req->ocount = v->v_pkt_out;
              req->ibytes = v->v_bytes_in;
              req->obytes = v->v_bytes_out;
      
              return (0);
      }
      
      int
      mrt_sysctl_vif(void *oldp, size_t *oldlenp)
      {
              caddr_t where = oldp;
              size_t needed, given;
              struct ifnet *ifp;
              struct vif *vifp;
              struct vifinfo vinfo;
      
              given = *oldlenp;
              needed = 0;
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if ((vifp = (struct vif *)ifp->if_mcast) == NULL)
                              continue;
      
                      vinfo.v_vifi = vifp->v_id;
                      vinfo.v_flags = vifp->v_flags;
                      vinfo.v_threshold = vifp->v_threshold;
                      vinfo.v_lcl_addr = vifp->v_lcl_addr;
                      vinfo.v_rmt_addr = vifp->v_rmt_addr;
                      vinfo.v_pkt_in = vifp->v_pkt_in;
                      vinfo.v_pkt_out = vifp->v_pkt_out;
                      vinfo.v_bytes_in = vifp->v_bytes_in;
                      vinfo.v_bytes_out = vifp->v_bytes_out;
      
                      needed += sizeof(vinfo);
                      if (where && needed <= given) {
                              int error;
      
                              error = copyout(&vinfo, where, sizeof(vinfo));
                              if (error)
                                      return (error);
                              where += sizeof(vinfo);
                      }
              }
              if (where) {
                      *oldlenp = needed;
                      if (given < needed)
                              return (ENOMEM);
              } else
                      *oldlenp = (11 * needed) / 10;
      
              return (0);
      }
      
      struct mfcsysctlarg {
              struct mfcinfo        *msa_minfos;
              size_t                 msa_len;
              size_t                 msa_needed;
      };
      
      int
      mrt_rtwalk_mfcsysctl(struct rtentry *rt, void *arg, unsigned int rtableid)
      {
              struct mfc                *mfc = (struct mfc *)rt->rt_llinfo;
              struct mfcsysctlarg        *msa = (struct mfcsysctlarg *)arg;
              struct ifnet                *ifp;
              struct vif                *v;
              struct mfcinfo                *minfo;
              int                         new = 0;
      
              /* Skip entries being removed. */
              if (mfc == NULL)
                      return (0);
      
              /* Skip non-multicast routes. */
              if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
                  (RTF_HOST | RTF_MULTICAST))
                      return (0);
      
              /* User just asked for the output size. */
              if (msa->msa_minfos == NULL) {
                      msa->msa_needed += sizeof(*minfo);
                      return (0);
              }
      
              /* Skip route with invalid interfaces. */
              if ((ifp = if_get(rt->rt_ifidx)) == NULL)
                      return (0);
              if ((v = (struct vif *)ifp->if_mcast) == NULL) {
                      if_put(ifp);
                      return (0);
              }
      
              for (minfo = msa->msa_minfos;
                   (uint8_t *)minfo < ((uint8_t *)msa->msa_minfos + msa->msa_len);
                   minfo++) {
                      /* Find a new entry or update old entry. */
                      if (minfo->mfc_origin.s_addr !=
                          satosin(rt->rt_gateway)->sin_addr.s_addr ||
                          minfo->mfc_mcastgrp.s_addr !=
                          satosin(rt_key(rt))->sin_addr.s_addr) {
                              if (minfo->mfc_origin.s_addr != 0 ||
                                  minfo->mfc_mcastgrp.s_addr != 0)
                                      continue;
      
                              new = 1;
                      }
      
                      minfo->mfc_origin = satosin(rt->rt_gateway)->sin_addr;
                      minfo->mfc_mcastgrp = satosin(rt_key(rt))->sin_addr;
                      minfo->mfc_parent = mfc->mfc_parent;
                      minfo->mfc_pkt_cnt += mfc->mfc_pkt_cnt;
                      minfo->mfc_byte_cnt += mfc->mfc_byte_cnt;
                      minfo->mfc_ttls[v->v_id] = mfc->mfc_ttl;
                      break;
              }
      
              if (new != 0)
                      msa->msa_needed += sizeof(*minfo);
      
              if_put(ifp);
      
              return (0);
      }
      
      int
      mrt_sysctl_mfc(void *oldp, size_t *oldlenp)
      {
              unsigned int                 rtableid;
              int                         error;
              struct mfcsysctlarg         msa;
      
              if (oldp != NULL && *oldlenp > MAXPHYS)
                      return (EINVAL);
      
              if (oldp != NULL)
                      msa.msa_minfos = malloc(*oldlenp, M_TEMP, M_WAITOK | M_ZERO);
              else
                      msa.msa_minfos = NULL;
      
              msa.msa_len = *oldlenp;
              msa.msa_needed = 0;
      
              for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) {
                      rtable_walk(rtableid, AF_INET, NULL, mrt_rtwalk_mfcsysctl,
                          &msa);
              }
      
              if (msa.msa_minfos != NULL && msa.msa_needed > 0 &&
                  (error = copyout(msa.msa_minfos, oldp, msa.msa_needed)) != 0) {
                      free(msa.msa_minfos, M_TEMP, *oldlenp);
                      return (error);
              }
      
              free(msa.msa_minfos, M_TEMP, *oldlenp);
              *oldlenp = msa.msa_needed;
      
              return (0);
      }
      
      /*
       * Enable multicast routing
       */
      int
      ip_mrouter_init(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              unsigned int rtableid = inp->inp_rtableid;
              int *v;
      
    1         if (so->so_type != SOCK_RAW ||
                  so->so_proto->pr_protocol != IPPROTO_IGMP)
                      return (EOPNOTSUPP);
      
    1         if (m == NULL || m->m_len < sizeof(int))
                      return (EINVAL);
      
              v = mtod(m, int *);
              if (*v != 1)
                      return (EINVAL);
      
              if (ip_mrouter[rtableid] != NULL ||
                  mrouterq[rtableid] != NULL)
                      return (EADDRINUSE);
      
              ip_mrouter[rtableid] = so;
              mrouterq[rtableid] = rt_timer_queue_create(MCAST_EXPIRE_FREQUENCY);
      
              return (0);
      }
      
      int
      mrouter_rtwalk_delete(struct rtentry *rt, void *arg, unsigned int rtableid)
      {
              /* Skip non-multicast routes. */
              if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
                  (RTF_HOST | RTF_MULTICAST))
                      return (0);
      
              /* Remove all timers related to this route. */
              rt_timer_remove_all(rt);
              rt_mcast_del(rt, rtableid);
              return (0);
      }
      
      /*
       * Disable multicast routing
       */
      int
      ip_mrouter_done(struct socket *so)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct ifnet *ifp;
              unsigned int rtableid = inp->inp_rtableid;
      
              NET_ASSERT_LOCKED();
      
              /* Delete all remaining installed multicast routes. */
              rtable_walk(rtableid, AF_INET, NULL, mrouter_rtwalk_delete, NULL);
      
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (ifp->if_rdomain != rtableid)
                              continue;
      
                      vif_delete(ifp);
              }
      
              mrt_api_config = 0;
      
              rt_timer_queue_destroy(mrouterq[rtableid]);
              mrouterq[rtableid] = NULL;
              ip_mrouter[rtableid] = NULL;
              mrt_count[rtableid] = 0;
      
              return (0);
      }
      
      int
      get_version(struct mbuf *m)
      {
              int *v = mtod(m, int *);
      
              *v = 0x0305;        /* XXX !!!! */
              m->m_len = sizeof(int);
              return (0);
      }
      
      /*
       * Configure API capabilities
       */
      int
      set_api_config(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct ifnet *ifp;
              u_int32_t *apival;
              unsigned int rtableid = inp->inp_rtableid;
      
              if (m == NULL || m->m_len < sizeof(u_int32_t))
                      return (EINVAL);
      
              apival = mtod(m, u_int32_t *);
      
              /*
               * We can set the API capabilities only if it is the first operation
               * after MRT_INIT. I.e.:
               *  - there are no vifs installed
               *  - the MFC table is empty
               */
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (ifp->if_rdomain != rtableid)
                              continue;
                      if (ifp->if_mcast == NULL)
                              continue;
      
                      *apival = 0;
                      return (EPERM);
              }
              if (mrt_count[rtableid] > 0) {
                      *apival = 0;
                      return (EPERM);
              }
      
              mrt_api_config = *apival & mrt_api_support;
              *apival = mrt_api_config;
      
              return (0);
      }
      
      /*
       * Get API capabilities
       */
      int
      get_api_support(struct mbuf *m)
      {
              u_int32_t *apival;
      
              if (m == NULL || m->m_len < sizeof(u_int32_t))
                      return (EINVAL);
      
              apival = mtod(m, u_int32_t *);
      
              *apival = mrt_api_support;
      
              return (0);
      }
      
      /*
       * Get API configured capabilities
       */
      int
      get_api_config(struct mbuf *m)
      {
              u_int32_t *apival;
      
              if (m == NULL || m->m_len < sizeof(u_int32_t))
                      return (EINVAL);
      
              apival = mtod(m, u_int32_t *);
      
              *apival = mrt_api_config;
      
              return (0);
      }
      
      static struct sockaddr_in sin = { sizeof(sin), AF_INET };
      
      int
      add_vif(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct vifctl *vifcp;
              struct vif *vifp;
              struct ifaddr *ifa;
              struct ifnet *ifp;
              struct ifreq ifr;
              int error;
              unsigned int rtableid = inp->inp_rtableid;
      
              NET_ASSERT_LOCKED();
      
              if (m == NULL || m->m_len < sizeof(struct vifctl))
                      return (EINVAL);
      
              vifcp = mtod(m, struct vifctl *);
              if (vifcp->vifc_vifi >= MAXVIFS)
                      return (EINVAL);
              if (in_nullhost(vifcp->vifc_lcl_addr))
                      return (EADDRNOTAVAIL);
              if (if_lookupbyvif(vifcp->vifc_vifi, rtableid) != NULL)
                      return (EADDRINUSE);
      
              /* Tunnels are no longer supported use gif(4) instead. */
              if (vifcp->vifc_flags & VIFF_TUNNEL)
                      return (EOPNOTSUPP);
              {
                      sin.sin_addr = vifcp->vifc_lcl_addr;
                      ifa = ifa_ifwithaddr(sintosa(&sin), rtableid);
                      if (ifa == NULL)
                              return (EADDRNOTAVAIL);
              }
      
              /* Use the physical interface associated with the address. */
              ifp = ifa->ifa_ifp;
              if (ifp->if_mcast != NULL)
                      return (EADDRINUSE);
      
              {
                      /* Make sure the interface supports multicast. */
                      if ((ifp->if_flags & IFF_MULTICAST) == 0)
                              return (EOPNOTSUPP);
      
                      /* Enable promiscuous reception of all IP multicasts. */
                      memset(&ifr, 0, sizeof(ifr));
                      satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in);
                      satosin(&ifr.ifr_addr)->sin_family = AF_INET;
                      satosin(&ifr.ifr_addr)->sin_addr = zeroin_addr;
                      error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
                      if (error)
                              return (error);
              }
      
              vifp = malloc(sizeof(*vifp), M_MRTABLE, M_WAITOK | M_ZERO);
              ifp->if_mcast = (caddr_t)vifp;
      
              vifp->v_id = vifcp->vifc_vifi;
              vifp->v_flags = vifcp->vifc_flags;
              vifp->v_threshold = vifcp->vifc_threshold;
              vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
              vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
      
              return (0);
      }
      
      int
      del_vif(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct ifnet *ifp;
              vifi_t *vifip;
              unsigned int rtableid = inp->inp_rtableid;
      
              NET_ASSERT_LOCKED();
      
              if (m == NULL || m->m_len < sizeof(vifi_t))
                      return (EINVAL);
      
              vifip = mtod(m, vifi_t *);
              if ((ifp = if_lookupbyvif(*vifip, rtableid)) == NULL)
                      return (EADDRNOTAVAIL);
      
              vif_delete(ifp);
              return (0);
      }
      
      void
      vif_delete(struct ifnet *ifp)
   23 {
              struct vif        *v;
              struct ifreq         ifr;
      
   23         if ((v = (struct vif *)ifp->if_mcast) == NULL)
                      return;
      
              ifp->if_mcast = NULL;
      
              memset(&ifr, 0, sizeof(ifr));
              satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in);
              satosin(&ifr.ifr_addr)->sin_family = AF_INET;
              satosin(&ifr.ifr_addr)->sin_addr = zeroin_addr;
              (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
      
              free(v, M_MRTABLE, sizeof(*v));
      }
      
      void
      mfc_expire_route(struct rtentry *rt, struct rttimer *rtt)
      {
              struct mfc        *mfc = (struct mfc *)rt->rt_llinfo;
              unsigned int         rtableid = rtt->rtt_tableid;
      
              /* Skip entry being deleted. */
              if (mfc == NULL)
                      return;
      
              DPRINTF("Route domain %d origin %#08X group %#08x interface %d "
                  "expire %s", rtt->rtt_tableid,
                  satosin(rt->rt_gateway)->sin_addr.s_addr,
                  satosin(rt_key(rt))->sin_addr.s_addr,
                  rt->rt_ifidx, mfc->mfc_expire ? "yes" : "no");
      
              /* Not expired, add it back to the queue. */
              if (mfc->mfc_expire == 0) {
                      mfc->mfc_expire = 1;
                      rt_timer_add(rt, mfc_expire_route, mrouterq[rtableid],
                          rtableid);
                      return;
              }
      
              /* Remove all timers related to this route. */
              rt_timer_remove_all(rt);
              rt_mcast_del(rt, rtableid);
      }
      
      int
      mfc_add_route(struct ifnet *ifp, struct sockaddr *origin,
          struct sockaddr *group, struct mfcctl2 *mfccp, int wait)
      {
              struct vif                *v = (struct vif *)ifp->if_mcast;
              struct rtentry                *rt;
              struct mfc                *mfc;
              unsigned int                 rtableid = ifp->if_rdomain;
      
              rt = rt_mcast_add(ifp, origin, group);
              if (rt == NULL)
                      return (EHOSTUNREACH);
      
              mfc = malloc(sizeof(*mfc), M_MRTABLE, wait | M_ZERO);
              if (mfc == NULL) {
                      DPRINTF("origin %#08X group %#08X parent %d (%s) "
                          "malloc failed",
                          satosin(origin)->sin_addr.s_addr,
                          satosin(group)->sin_addr.s_addr,
                          mfccp->mfcc_parent, ifp->if_xname);
                      rt_mcast_del(rt, rtableid);
                      return (ENOMEM);
              }
      
              rt->rt_llinfo = (caddr_t)mfc;
      
              rt_timer_add(rt, mfc_expire_route, mrouterq[rtableid],
                  rtableid);
      
              mfc->mfc_parent = mfccp->mfcc_parent;
              mfc->mfc_pkt_cnt = 0;
              mfc->mfc_byte_cnt = 0;
              mfc->mfc_wrong_if = 0;
              mfc->mfc_ttl = mfccp->mfcc_ttls[v->v_id];
              mfc->mfc_flags = mfccp->mfcc_flags[v->v_id] & mrt_api_config &
                  MRT_MFC_FLAGS_ALL;
              mfc->mfc_expire = 0;
      
              /* set the RP address */
              if (mrt_api_config & MRT_MFC_RP)
                      mfc->mfc_rp = mfccp->mfcc_rp;
              else
                      mfc->mfc_rp = zeroin_addr;
      
              rtfree(rt);
      
              return (0);
      }
      
      void
      update_mfc_params(struct mfcctl2 *mfccp, int wait, unsigned int rtableid)
      {
              struct rtentry                *rt;
              struct mfc                *mfc;
              struct ifnet                *ifp;
              int                         i;
              struct sockaddr_in         osin, msin;
      
              memset(&osin, 0, sizeof(osin));
              osin.sin_len = sizeof(osin);
              osin.sin_family = AF_INET;
              osin.sin_addr = mfccp->mfcc_origin;
      
              memset(&msin, 0, sizeof(msin));
              msin.sin_len = sizeof(msin);
              msin.sin_family = AF_INET;
              msin.sin_addr = mfccp->mfcc_mcastgrp;
      
              for (i = 0; i < MAXVIFS; i++) {
                      /* Don't add/del upstream routes here. */
                      if (i == mfccp->mfcc_parent)
                              continue;
      
                      /* Test for vif existence and then update the entry. */
                      if ((ifp = if_lookupbyvif(i, rtableid)) == NULL)
                              continue;
      
                      rt = mfc_find(ifp, &mfccp->mfcc_origin,
                          &mfccp->mfcc_mcastgrp, rtableid);
      
                      /* vif not configured or removed. */
                      if (mfccp->mfcc_ttls[i] == 0) {
                              /* Route doesn't exist, nothing to do. */
                              if (rt == NULL)
                                      continue;
      
                              DPRINTF("del route (group %#08X) for vif %d (%s)",
                                  mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
                              rt_timer_remove_all(rt);
                              rt_mcast_del(rt, rtableid);
                              continue;
                      }
      
                      /* Route exists, look for changes. */
                      if (rt != NULL) {
                              mfc = (struct mfc *)rt->rt_llinfo;
                              /* Skip route being deleted. */
                              if (mfc == NULL) {
                                      rtfree(rt);
                                      continue;
                              }
      
                              /* No new changes to apply. */
                              if (mfccp->mfcc_ttls[i] == mfc->mfc_ttl &&
                                  mfccp->mfcc_parent == mfc->mfc_parent) {
                                      rtfree(rt);
                                      continue;
                              }
      
                              DPRINTF("update route (group %#08X) for vif %d (%s)",
                                  mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
                              mfc->mfc_ttl = mfccp->mfcc_ttls[i];
                              mfc->mfc_parent = mfccp->mfcc_parent;
                              rtfree(rt);
                              continue;
                      }
      
                      DPRINTF("add route (group %#08X) for vif %d (%s)",
                          mfccp->mfcc_mcastgrp.s_addr, i, ifp->if_xname);
      
                      mfc_add_route(ifp, sintosa(&osin), sintosa(&msin),
                          mfccp, wait);
              }
      
              /* Create route for the parent interface. */
              if ((ifp = if_lookupbyvif(mfccp->mfcc_parent, rtableid)) == NULL) {
                      DPRINTF("failed to find upstream interface %d",
                          mfccp->mfcc_parent);
                      return;
              }
      
              /* We already have a route, nothing to do here. */
              if ((rt = mfc_find(ifp, &mfccp->mfcc_origin,
                  &mfccp->mfcc_mcastgrp, rtableid)) != NULL) {
                      rtfree(rt);
                      return;
              }
      
              DPRINTF("add upstream route (group %#08X) for if %s",
                  mfccp->mfcc_mcastgrp.s_addr, ifp->if_xname);
              mfc_add_route(ifp, sintosa(&osin), sintosa(&msin), mfccp, wait);
      }
      
      int
      mfc_add(struct mfcctl2 *mfcctl2, struct in_addr *origin,
          struct in_addr *group, int vidx, unsigned int rtableid, int wait)
      {
              struct ifnet                *ifp;
              struct vif                *v;
              struct mfcctl2                 mfcctl;
      
              ifp = if_lookupbyvif(vidx, rtableid);
              if (ifp == NULL ||
                  (v = (struct vif *)ifp->if_mcast) == NULL)
                      return (EHOSTUNREACH);
      
              memset(&mfcctl, 0, sizeof(mfcctl));
              if (mfcctl2 == NULL) {
                      mfcctl.mfcc_origin = *origin;
                      mfcctl.mfcc_mcastgrp = *group;
                      mfcctl.mfcc_parent = vidx;
              } else
                      memcpy(&mfcctl, mfcctl2, sizeof(mfcctl));
      
              update_mfc_params(&mfcctl, wait, rtableid);
      
              return (0);
      }
      
      int
      add_mfc(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct mfcctl2 mfcctl2;
              int mfcctl_size = sizeof(struct mfcctl);
              unsigned int rtableid = inp->inp_rtableid;
      
              NET_ASSERT_LOCKED();
      
              if (mrt_api_config & MRT_API_FLAGS_ALL)
                      mfcctl_size = sizeof(struct mfcctl2);
      
              if (m == NULL || m->m_len < mfcctl_size)
                      return (EINVAL);
      
              /*
               * select data size depending on API version.
               */
              if (mrt_api_config & MRT_API_FLAGS_ALL) {
                      struct mfcctl2 *mp2 = mtod(m, struct mfcctl2 *);
                      memcpy((caddr_t)&mfcctl2, mp2, sizeof(*mp2));
              } else {
                      struct mfcctl *mp = mtod(m, struct mfcctl *);
                      memcpy((caddr_t)&mfcctl2, mp, sizeof(*mp));
                      memset((caddr_t)&mfcctl2 + sizeof(struct mfcctl), 0,
                          sizeof(mfcctl2) - sizeof(struct mfcctl));
              }
      
              if (mfc_add(&mfcctl2, &mfcctl2.mfcc_origin, &mfcctl2.mfcc_mcastgrp,
                  mfcctl2.mfcc_parent, rtableid, M_WAITOK) == -1)
                      return (EINVAL);
      
              return (0);
      }
      
      int
      del_mfc(struct socket *so, struct mbuf *m)
      {
              struct inpcb *inp = sotoinpcb(so);
              struct rtentry *rt;
              struct mfcctl2 mfcctl2;
              int mfcctl_size = sizeof(struct mfcctl);
              struct mfcctl *mp;
              unsigned int rtableid = inp->inp_rtableid;
      
              NET_ASSERT_LOCKED();
      
              /*
               * XXX: for deleting MFC entries the information in entries
               * of size "struct mfcctl" is sufficient.
               */
      
              if (m == NULL || m->m_len < mfcctl_size)
                      return (EINVAL);
      
              mp = mtod(m, struct mfcctl *);
      
              memcpy((caddr_t)&mfcctl2, mp, sizeof(*mp));
              memset((caddr_t)&mfcctl2 + sizeof(struct mfcctl), 0,
                  sizeof(mfcctl2) - sizeof(struct mfcctl));
      
              DPRINTF("origin %#08X group %#08X rtableid %d",
                  mfcctl2.mfcc_origin.s_addr, mfcctl2.mfcc_mcastgrp.s_addr, rtableid);
      
              while ((rt = mfc_find(NULL, &mfcctl2.mfcc_origin,
                  &mfcctl2.mfcc_mcastgrp, rtableid)) != NULL) {
                      /* Remove all timers related to this route. */
                      rt_timer_remove_all(rt);
                      rt_mcast_del(rt, rtableid);
              }
      
              return (0);
      }
      
      int
      socket_send(struct socket *s, struct mbuf *mm, struct sockaddr_in *src)
      {
              if (s != NULL) {
                      if (sbappendaddr(s, &s->so_rcv, sintosa(src), mm, NULL) != 0) {
                              sorwakeup(s);
                              return (0);
                      }
              }
              m_freem(mm);
              return (-1);
      }
      
      /*
       * IP multicast forwarding function. This function assumes that the packet
       * pointed to by "ip" has arrived on (or is about to be sent to) the interface
       * pointed to by "ifp", and the packet is to be relayed to other networks
       * that have members of the packet's destination IP multicast group.
       *
       * The packet is returned unscathed to the caller, unless it is
       * erroneous, in which case a non-zero return value tells the caller to
       * discard it.
       */
      
      #define IP_HDR_LEN  20        /* # bytes of fixed IP header (excluding options) */
      #define TUNNEL_LEN  12  /* # bytes of IP option for tunnel encapsulation  */
      
      int
      ip_mforward(struct mbuf *m, struct ifnet *ifp)
      {
              struct ip *ip = mtod(m, struct ip *);
              struct vif *v;
              struct rtentry *rt;
              static int srctun = 0;
              struct mbuf *mm;
              unsigned int rtableid = ifp->if_rdomain;
      
              if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
                  ((u_char *)(ip + 1))[1] != IPOPT_LSRR) {
                      /*
                       * Packet arrived via a physical interface or
                       * an encapsulated tunnel or a register_vif.
                       */
              } else {
                      /*
                       * Packet arrived through a source-route tunnel.
                       * Source-route tunnels are no longer supported.
                       */
                      if ((srctun++ % 1000) == 0)
                              log(LOG_ERR, "ip_mforward: received source-routed "
                                  "packet from %x\n", ntohl(ip->ip_src.s_addr));
                      return (EOPNOTSUPP);
              }
      
              /*
               * Don't forward a packet with time-to-live of zero or one,
               * or a packet destined to a local-only group.
               */
              if (ip->ip_ttl <= 1 || IN_LOCAL_GROUP(ip->ip_dst.s_addr))
                      return (0);
      
              /*
               * Determine forwarding vifs from the forwarding cache table
               */
              ++mrtstat.mrts_mfc_lookups;
              rt = mfc_find(NULL, &ip->ip_src, &ip->ip_dst, rtableid);
      
              /* Entry exists, so forward if necessary */
              if (rt != NULL) {
                      return (ip_mdq(m, ifp, rt));
              } else {
                      /*
                       * If we don't have a route for packet's origin,
                       * Make a copy of the packet & send message to routing daemon
                       */
                      int hlen = ip->ip_hl << 2;
      
                      ++mrtstat.mrts_mfc_misses;
                      mrtstat.mrts_no_route++;
      
                      {
                              struct igmpmsg *im;
      
                              /*
                               * Locate the vifi for the incoming interface for
                               * this packet.
                               * If none found, drop packet.
                               */
                              if ((v = (struct vif *)ifp->if_mcast) == NULL)
                                      return (EHOSTUNREACH);
                              /*
                               * Make a copy of the header to send to the user level
                               * process
                               */
                              mm = m_copym(m, 0, hlen, M_NOWAIT);
                              if (mm == NULL ||
                                  (mm = m_pullup(mm, hlen)) == NULL)
                                      return (ENOBUFS);
      
                              /*
                               * Send message to routing daemon to install
                               * a route into the kernel table
                               */
      
                              im = mtod(mm, struct igmpmsg *);
                              im->im_msgtype = IGMPMSG_NOCACHE;
                              im->im_mbz = 0;
                              im->im_vif = v->v_id;
      
                              mrtstat.mrts_upcalls++;
      
                              sin.sin_addr = ip->ip_src;
                              if (socket_send(ip_mrouter[rtableid], mm, &sin) < 0) {
                                      log(LOG_WARNING, "ip_mforward: ip_mrouter "
                                          "socket queue full\n");
                                      ++mrtstat.mrts_upq_sockfull;
                                      return (ENOBUFS);
                              }
      
                              mfc_add(NULL, &ip->ip_src, &ip->ip_dst, v->v_id,
                                  rtableid, M_NOWAIT);
                      }
      
                      return (0);
              }
      }
      
      /*
       * Packet forwarding routine once entry in the cache is made
       */
      int
      ip_mdq(struct mbuf *m, struct ifnet *ifp0, struct rtentry *rt)
      {
              struct ip  *ip = mtod(m, struct ip *);
              struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
              struct vif *v = (struct vif *)ifp0->if_mcast;
              struct ifnet *ifp;
              struct mbuf *mc;
              struct ip_moptions imo;
      
              /* Sanity check: we have all promised pointers. */
              if (v == NULL || mfc == NULL) {
                      rtfree(rt);
                      return (EHOSTUNREACH);
              }
      
              /*
               * Don't forward if it didn't arrive from the parent vif for its origin.
               */
              if (mfc->mfc_parent != v->v_id) {
                      /* came in the wrong interface */
                      ++mrtstat.mrts_wrong_if;
                      mfc->mfc_wrong_if++;
                      rtfree(rt);
                      return (0);
              }
      
              /* If I sourced this packet, it counts as output, else it was input. */
              if (in_hosteq(ip->ip_src, v->v_lcl_addr)) {
                      v->v_pkt_out++;
                      v->v_bytes_out += m->m_pkthdr.len;
              } else {
                      v->v_pkt_in++;
                      v->v_bytes_in += m->m_pkthdr.len;
              }
      
              /*
               * For each vif, decide if a copy of the packet should be forwarded.
               * Forward if:
               *                - the ttl exceeds the vif's threshold
               *                - there are group members downstream on interface
               */
              do {
                      /* Don't consider non multicast routes. */
                      if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) !=
                          (RTF_HOST | RTF_MULTICAST))
                              continue;
      
                      mfc = (struct mfc *)rt->rt_llinfo;
                      if (mfc == NULL)
                              continue;
      
                      mfc->mfc_pkt_cnt++;
                      mfc->mfc_byte_cnt += m->m_pkthdr.len;
      
                      /* Don't let this route expire. */
                      mfc->mfc_expire = 0;
      
                      if (ip->ip_ttl <= mfc->mfc_ttl)
                              continue;
                      if ((ifp = if_get(rt->rt_ifidx)) == NULL)
                              continue;
      
                      /* Sanity check: did we configure this? */
                      if ((v = (struct vif *)ifp->if_mcast) == NULL) {
                              if_put(ifp);
                              continue;
                      }
      
                      /* Don't send in the upstream interface. */
                      if (mfc->mfc_parent == v->v_id) {
                              if_put(ifp);
                              continue;
                      }
      
                      v->v_pkt_out++;
                      v->v_bytes_out += m->m_pkthdr.len;
      
                      /*
                       * Make a new reference to the packet; make sure
                       * that the IP header is actually copied, not
                       * just referenced, so that ip_output() only
                       * scribbles on the copy.
                       */
                      mc = m_dup_pkt(m, max_linkhdr, M_NOWAIT);
                      if (mc == NULL) {
                              if_put(ifp);
                              rtfree(rt);
                              return (ENOBUFS);
                      }
      
                      /*
                       * if physical interface option, extract the options
                       * and then send
                       */
                      imo.imo_ifidx = rt->rt_ifidx;
                      imo.imo_ttl = ip->ip_ttl - IPTTLDEC;
                      imo.imo_loop = 1;
      
                      ip_output(mc, NULL, NULL, IP_FORWARDING, &imo, NULL, 0);
                      if_put(ifp);
              } while ((rt = rtable_iterate(rt)) != NULL);
      
              return (0);
      }
      
      struct ifnet *
      if_lookupbyvif(vifi_t vifi, unsigned int rtableid)
      {
              struct vif        *v;
              struct ifnet        *ifp;
      
              TAILQ_FOREACH(ifp, &ifnet, if_list) {
                      if (ifp->if_rdomain != rtableid)
                              continue;
                      if ((v = (struct vif *)ifp->if_mcast) == NULL)
                              continue;
                      if (v->v_id != vifi)
                              continue;
      
                      return (ifp);
              }
      
              return (NULL);
      }
      
      struct rtentry *
      rt_mcast_add(struct ifnet *ifp, struct sockaddr *origin, struct sockaddr *group)
      {
              struct ifaddr                *ifa;
              int                         rv;
              unsigned int                 rtableid = ifp->if_rdomain;
      
              TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                      if (ifa->ifa_addr->sa_family == AF_INET)
                              break;
              }
              if (ifa == NULL) {
                      DPRINTF("ifa == NULL");
                      return (NULL);
              }
      
              rv = rt_ifa_add(ifa, RTF_HOST | RTF_MULTICAST | RTF_MPATH,
                  group, ifp->if_rdomain);
              if (rv != 0) {
                      DPRINTF("rt_ifa_add failed (%d)", rv);
                      return (NULL);
              }
      
              mrt_count[rtableid]++;
      
              return (mfc_find(ifp, NULL, &satosin(group)->sin_addr, rtableid));
      }
      
      int
      rt_mcast_del(struct rtentry *rt, unsigned int rtableid)
      {
              struct ifnet                *ifp;
              int                         rv;
      
              free(rt->rt_llinfo, M_MRTABLE, sizeof(struct mfc));
              rt->rt_llinfo = NULL;
      
              if ((ifp = if_get(rt->rt_ifidx)) == NULL) {
                      DPRINTF("if_get(%d) failed", rt->rt_ifidx);
                      rtfree(rt);
                      return (ENOENT);
              }
      
              rv = rtdeletemsg(rt, ifp, rtableid);
              if_put(ifp);
              if (rv != 0) {
                      DPRINTF("rtdeletemsg failed (%d)", rv);
                      rtfree(rt);
                      return (rv);
              }
      
              mrt_count[rtableid]--;
      
              return (0);
      }
      /*        $OpenBSD: vm_machdep.c,v 1.43 2018/08/21 13:10:13 bluhm Exp $        */
      /*        $NetBSD: vm_machdep.c,v 1.1 2003/04/26 18:39:33 fvdl Exp $        */
      
      /*-
       * Copyright (c) 1995 Charles M. Hannum.  All rights reserved.
       * Copyright (c) 1982, 1986 The Regents of the University of California.
       * Copyright (c) 1989, 1990 William Jolitz
       * All rights reserved.
       *
       * This code is derived from software contributed to Berkeley by
       * the Systems Programming Group of the University of Utah Computer
       * Science Department, and William Jolitz.
       *
       * 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.
       *
       *        @(#)vm_machdep.c        7.3 (Berkeley) 5/13/91
       */
      
      /*
       *        Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/proc.h>
      #include <sys/buf.h>
      #include <sys/user.h>
      
      #include <uvm/uvm_extern.h>
      
      #include <machine/cpu.h>
      #include <machine/fpu.h>
      
      void setguardpage(struct proc *);
      
      /*
       * Finish a fork operation, with process p2 nearly set up.
       * Copy and update the kernel stack and pcb, making the child
       * ready to run, and marking it so that it can return differently
       * than the parent.
       */
      void
      cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb,
          void (*func)(void *), void *arg)
    2 {
              struct cpu_info *ci = curcpu();
              struct pcb *pcb = &p2->p_addr->u_pcb;
              struct pcb *pcb1 = &p1->p_addr->u_pcb;
              struct trapframe *tf;
              struct switchframe *sf;
      
              /* Save the fpu h/w state to p1's pcb so that we can copy it. */
    2         if (p1 != &proc0 && (ci->ci_flags & CPUF_USERXSTATE))
                      fpusave(&pcb1->pcb_savefpu);
      
              p2->p_md.md_flags = p1->p_md.md_flags;
      
      #ifdef DIAGNOSTIC
              if (p1 != curproc && p1 != &proc0)
                      panic("cpu_fork: curproc");
      #endif
              *pcb = *pcb1;
      
              /*
               * Activate the address space.
               */
              pmap_activate(p2);
      
              /* Record where this process's kernel stack is */
              pcb->pcb_kstack = (u_int64_t)p2->p_addr + USPACE - 16 -
                  (arc4random() & PAGE_MASK & ~_STACKALIGNBYTES);
      
              /*
               * Copy the trapframe.
               */
              p2->p_md.md_regs = tf = (struct trapframe *)pcb->pcb_kstack - 1;
              *tf = *p1->p_md.md_regs;
      
              setguardpage(p2);
      
              /*
               * If specified, give the child a different stack and/or TCB
               */
    2         if (stack != NULL)
                      tf->tf_rsp = (u_int64_t)stack;
    2         if (tcb != NULL)
                      pcb->pcb_fsbase = (u_int64_t)tcb;
      
              sf = (struct switchframe *)tf - 1;
              sf->sf_r12 = (u_int64_t)func;
              sf->sf_r13 = (u_int64_t)arg;
              sf->sf_rip = (u_int64_t)proc_trampoline;
              pcb->pcb_rsp = (u_int64_t)sf;
              pcb->pcb_rbp = 0;
      }
      
      /*
       * cpu_exit is called as the last action during exit.
       *
       * We clean up a little and then call sched_exit() with the old proc as an
       * argument.
       */
      void
      cpu_exit(struct proc *p)
      {
              pmap_deactivate(p);
              sched_exit(p);
      }
      
      /*
       * Set a red zone in the kernel stack after the u. area.
       */
      void
      setguardpage(struct proc *p)
      {
              pmap_remove(pmap_kernel(), (vaddr_t)p->p_addr + PAGE_SIZE,
                  (vaddr_t)p->p_addr + 2 * PAGE_SIZE);
              pmap_update(pmap_kernel());
      }
      
      /*
       * Map a user I/O request into kernel virtual address space.
       * Note: the pages are already locked by uvm_vslock(), so we
       * do not need to pass an access_type to pmap_enter().   
       */
      void
      vmapbuf(struct buf *bp, vsize_t len)
   19 {
              vaddr_t faddr, taddr, off;
              paddr_t fpa;
      
              if ((bp->b_flags & B_PHYS) == 0)
                      panic("vmapbuf");
              faddr = trunc_page((vaddr_t)(bp->b_saveaddr = bp->b_data));
              off = (vaddr_t)bp->b_data - faddr;
              len = round_page(off + len);
              taddr= uvm_km_valloc_wait(phys_map, len);
              bp->b_data = (caddr_t)(taddr + off);
              /*
               * The region is locked, so we expect that pmap_pte() will return
               * non-NULL.
               * XXX: unwise to expect this in a multithreaded environment.
               * anything can happen to a pmap between the time we lock a 
               * region, release the pmap lock, and then relock it for
               * the pmap_extract().
               *
               * no need to flush TLB since we expect nothing to be mapped
               * where we we just allocated (TLB will be flushed when our
               * mapping is removed).
               */
   19         while (len) {
                      (void) pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
                          faddr, &fpa);
                      pmap_kenter_pa(taddr, fpa, PROT_READ | PROT_WRITE);
                      faddr += PAGE_SIZE;
                      taddr += PAGE_SIZE;
                      len -= PAGE_SIZE;
              }
      }
      
      /*
       * Unmap a previously-mapped user I/O request.
       */
      void
      vunmapbuf(struct buf *bp, vsize_t len)
   19 {
              vaddr_t addr, off;
      
              if ((bp->b_flags & B_PHYS) == 0)
                      panic("vunmapbuf");
   19         addr = trunc_page((vaddr_t)bp->b_data);
              off = (vaddr_t)bp->b_data - addr;
              len = round_page(off + len);
              pmap_kremove(addr, len);
              pmap_update(pmap_kernel());
              uvm_km_free_wakeup(phys_map, addr, len);
              bp->b_data = bp->b_saveaddr;
              bp->b_saveaddr = 0;
      }
      
      void *
      tcb_get(struct proc *p)
      {
              return ((void *)p->p_addr->u_pcb.pcb_fsbase);
      }
      
      void
      tcb_set(struct proc *p, void *tcb)
      {
              KASSERT(p == curproc);
              reset_segs();
              p->p_addr->u_pcb.pcb_fsbase = (u_int64_t)tcb;
      }
      /*        $OpenBSD: pf_syncookies.c,v 1.7 2018/09/10 15:54:28 henning Exp $ */
      
      /* Copyright (c) 2016,2017 Henning Brauer <henning@openbsd.org>
       * Copyright (c) 2016 Alexandr Nedvedicky <sashan@openbsd.org>
       *
       * syncookie parts based on FreeBSD sys/netinet/tcp_syncache.c
       *
       * Copyright (c) 2001 McAfee, Inc.
       * Copyright (c) 2006,2013 Andre Oppermann, Internet Business Solutions AG
       * All rights reserved.
       *
       * This software was developed for the FreeBSD Project by Jonathan Lemon
       * and McAfee Research, the Security Research Division of McAfee, Inc. under
       * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
       * DARPA CHATS research program. [2001 McAfee, Inc.]
       *
       * 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.
       *
       * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
       */
      
      /*
       * when we're under synflood, we use syncookies to prevent state table
       * exhaustion. Trigger for the synflood mode is the number of half-open
       * connections in the state table.
       * We leave synflood mode when the number of half-open states - including
       * in-flight syncookies - drops far enough again
       */
       
      /*
       * syncookie enabled Initial Sequence Number:
       *  24 bit MAC
       *   3 bit WSCALE index
       *   3 bit MSS index
       *   1 bit SACK permitted
       *   1 bit odd/even secret
       *
       * References:
       *  RFC4987 TCP SYN Flooding Attacks and Common Mitigations
       *  http://cr.yp.to/syncookies.html    (overview)
       *  http://cr.yp.to/syncookies/archive (details)
       */
      
      #include "pflog.h"
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/mbuf.h>
      #include <sys/filio.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/kernel.h>
      #include <sys/time.h>
      #include <sys/pool.h>
      #include <sys/proc.h>
      #include <sys/rwlock.h>
      #include <sys/syslog.h>
      
      #include <net/if.h>
      #include <net/if_var.h>
      #include <net/if_types.h>
      #include <net/route.h>
      
      #include <netinet/in.h>
      #include <netinet/ip.h>
      #include <netinet/ip_var.h>
      #include <netinet/tcp.h>
      #include <netinet/tcp_seq.h>
      #include <netinet/udp.h>
      #include <netinet/ip_icmp.h>
      #include <netinet/in_pcb.h>
      #include <netinet/tcp_timer.h>
      #include <netinet/tcp_var.h>
      #include <netinet/tcp_fsm.h>
      #include <netinet/udp_var.h>
      #include <netinet/icmp_var.h>
      #include <netinet/ip_divert.h>
      
      #include <net/pfvar.h>
      #include <net/pfvar_priv.h>
      
      #if NPFLOG > 0
      #include <net/if_pflog.h>
      #endif        /* NPFLOG > 0 */
      
      union pf_syncookie {
              uint8_t                cookie;
              struct {
                      uint8_t        oddeven:1,
                              sack_ok:1,
                              wscale_idx:3,
                              mss_idx:3;
              } flags;
      };
      
      #define        PF_SYNCOOKIE_SECRET_SIZE        SIPHASH_KEY_LENGTH
      #define        PF_SYNCOOKIE_SECRET_LIFETIME        15 /* seconds */
      
      static struct {
              struct timeout        keytimeout;
              volatile uint        oddeven;
              SIPHASH_KEY        key[2];
              uint32_t        hiwat;        /* absolute; # of states */
              uint32_t        lowat;
      } pf_syncookie_status;
      
      void                pf_syncookie_rotate(void *);
      void                pf_syncookie_newkey(void);
      uint32_t        pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie,
                          uint32_t);
      uint32_t        pf_syncookie_generate(struct pf_pdesc *, uint16_t);
      
      void
      pf_syncookies_init(void)
      {
              timeout_set(&pf_syncookie_status.keytimeout,
                  pf_syncookie_rotate, NULL);
              pf_syncookie_status.hiwat = PFSTATE_HIWAT * PF_SYNCOOKIES_HIWATPCT/100;
              pf_syncookie_status.lowat = PFSTATE_HIWAT * PF_SYNCOOKIES_LOWATPCT/100;
              pf_syncookies_setmode(PF_SYNCOOKIES_NEVER);
      }
      
      int
      pf_syncookies_setmode(u_int8_t mode)
      {
              if (mode > PF_SYNCOOKIES_MODE_MAX)
                      return (EINVAL);
      
              if (pf_status.syncookies_mode == mode)
                      return (0);
      
              pf_status.syncookies_mode = mode;
              if (pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) {
                      pf_syncookie_newkey();
                      pf_status.syncookies_active = 1;
              }
              return (0);
      }
      
      int
      pf_syncookies_setwats(u_int32_t hiwat, u_int32_t lowat)
      {
              if (lowat > hiwat)
                      return (EINVAL);
      
              pf_syncookie_status.hiwat = hiwat;
              pf_syncookie_status.lowat = lowat;
              return (0);
      }
      
      int
      pf_syncookies_getwats(struct pfioc_synflwats *wats)
      {
              wats->hiwat = pf_syncookie_status.hiwat;
              wats->lowat = pf_syncookie_status.lowat;
              return (0);
      }
      
      int
      pf_synflood_check(struct pf_pdesc *pd)
   17 {
              KASSERT (pd->proto == IPPROTO_TCP);
      
   17         if (pd->m && (pd->m->m_pkthdr.pf.tag & PF_TAG_SYNCOOKIE_RECREATED))
                      return (0);
      
   17         if (pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE)
                      return (pf_status.syncookies_mode);
      
              if (!pf_status.syncookies_active &&
                  pf_status.states_halfopen > pf_syncookie_status.hiwat) {
                      pf_syncookie_newkey();
                      pf_status.syncookies_active = 1;
                      DPFPRINTF(LOG_WARNING,
                          "synflood detected, enabling syncookies");
                      pf_status.lcounters[LCNT_SYNFLOODS]++;
              }
      
              return (pf_status.syncookies_active);
      }
      
      void
      pf_syncookie_send(struct pf_pdesc *pd)
      {
              uint16_t        mss;
              uint32_t        iss;
      
              mss = max(tcp_mssdflt, pf_get_mss(pd));
              iss = pf_syncookie_generate(pd, mss);
              pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
                  iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
                  0, 1, 0, pd->rdomain);
              pf_status.syncookies_inflight[pf_syncookie_status.oddeven]++;
              pf_status.lcounters[LCNT_SYNCOOKIES_SENT]++;
      }
      
      uint8_t
      pf_syncookie_validate(struct pf_pdesc *pd)
    2 {
              uint32_t                 hash, ack, seq;
              union pf_syncookie         cookie;
      
              KASSERT(pd->proto == IPPROTO_TCP);
      
              seq = ntohl(pd->hdr.tcp.th_seq) - 1;
              ack = ntohl(pd->hdr.tcp.th_ack) - 1;
              cookie.cookie = (ack & 0xff) ^ (ack >> 24);
      
              /* we don't know oddeven before setting the cookie (union) */
    2         if (pf_status.syncookies_inflight[cookie.flags.oddeven] == 0)
                      return (0);
      
              hash = pf_syncookie_mac(pd, cookie, seq);
              if ((ack & ~0xff) != (hash & ~0xff))
                      return (0);
      
              pf_status.syncookies_inflight[cookie.flags.oddeven]--;
              pf_status.lcounters[LCNT_SYNCOOKIES_VALID]++;
              return (1);
      }
      
      /*
       * all following functions private
       */
      void
      pf_syncookie_rotate(void *arg)
      {
              /* do we want to disable syncookies? */
              if (pf_status.syncookies_active &&
                  ((pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE &&
                  pf_status.states_halfopen + pf_status.syncookies_inflight[0] +
                  pf_status.syncookies_inflight[1] < pf_syncookie_status.lowat) ||
                  pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER)) {
                      pf_status.syncookies_active = 0;
                      DPFPRINTF(LOG_WARNING, "syncookies disabled");
              }
      
              /* nothing in flight any more? delete keys and return */
              if (!pf_status.syncookies_active &&
                  pf_status.syncookies_inflight[0] == 0 &&
                  pf_status.syncookies_inflight[1] == 0) {
                      memset(&pf_syncookie_status.key[0], 0,
                          PF_SYNCOOKIE_SECRET_SIZE);
                      memset(&pf_syncookie_status.key[1], 0,
                          PF_SYNCOOKIE_SECRET_SIZE);
                      return;
              }
      
              /* new key, including timeout */
              pf_syncookie_newkey();
      }
      
      void
      pf_syncookie_newkey(void)
      {
              pf_syncookie_status.oddeven = (pf_syncookie_status.oddeven + 1) & 0x1;
              pf_status.syncookies_inflight[pf_syncookie_status.oddeven] = 0;
              arc4random_buf(&pf_syncookie_status.key[pf_syncookie_status.oddeven],
                  PF_SYNCOOKIE_SECRET_SIZE);
              timeout_add_sec(&pf_syncookie_status.keytimeout,
                  PF_SYNCOOKIE_SECRET_LIFETIME);
      }
      
      /*
       * Distribution and probability of certain MSS values.  Those in between are
       * rounded down to the next lower one.
       * [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011]
       *   .2%  .3%   5%    7%    7%    20%   15%   45%
       */
      static int pf_syncookie_msstab[] = 
          { 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 };
      
      /*
       * Distribution and probability of certain WSCALE values.
       * The absence of the WSCALE option is encoded with index zero.
       * [WSCALE values histograms, Allman, 2012]
       *                                  X 10 10 35  5  6 14 10%   by host
       *                                  X 11  4  5  5 18 49  3%   by connections
       */
      static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 };
      
      uint32_t
      pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq)
      {
              SIPHASH_CTX        ctx;
              uint32_t        siphash[2];
      
              KASSERT(pd->proto == IPPROTO_TCP);
      
              SipHash24_Init(&ctx, &pf_syncookie_status.key[cookie.flags.oddeven]);
      
              switch (pd->af) {
              case AF_INET:
                      SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v4));
                      SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v4));
                      break;
              case AF_INET6:
                      SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v6));
                      SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v6));
                      break;
              default:
                      panic("unknown address family");
              }
      
              SipHash24_Update(&ctx, pd->sport, sizeof(*pd->sport));
              SipHash24_Update(&ctx, pd->dport, sizeof(*pd->dport));
              SipHash24_Update(&ctx, &seq, sizeof(seq));
              SipHash24_Update(&ctx, &cookie, sizeof(cookie));
              SipHash24_Final((uint8_t *)&siphash, &ctx);
      
              return (siphash[0] ^ siphash[1]);
      }
      
      uint32_t
      pf_syncookie_generate(struct pf_pdesc *pd, uint16_t mss)
      {
              uint8_t                         i, wscale;
              uint32_t                 iss, hash;
              union pf_syncookie         cookie;
      
              cookie.cookie = 0;
      
              /* map MSS */
              for (i = nitems(pf_syncookie_msstab) - 1;
                  pf_syncookie_msstab[i] > mss && i > 0; i--)
                      /* nada */;
              cookie.flags.mss_idx = i;
      
              /* map WSCALE */
              wscale = pf_get_wscale(pd);
              for (i = nitems(pf_syncookie_wstab) - 1;
                  pf_syncookie_wstab[i] > wscale && i > 0; i--)
                      /* nada */;
              cookie.flags.wscale_idx = i;
              cookie.flags.sack_ok = 0;        /* XXX */
      
              cookie.flags.oddeven = pf_syncookie_status.oddeven;
              hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq));
      
              /*
               * Put the flags into the hash and XOR them to get better ISS number
               * variance.  This doesn't enhance the cryptographic strength and is
               * done to prevent the 8 cookie bits from showing up directly on the
               * wire.
               */
              iss = hash & ~0xff;
              iss |= cookie.cookie ^ (hash >> 24);
      
              return (iss);
      }
      
      struct mbuf *
      pf_syncookie_recreate_syn(struct pf_pdesc *pd)
      {
              uint8_t                         wscale;
              uint16_t                 mss;
              uint32_t                 ack, seq;
              union pf_syncookie         cookie;
      
              seq = ntohl(pd->hdr.tcp.th_seq) - 1;
              ack = ntohl(pd->hdr.tcp.th_ack) - 1;
              cookie.cookie = (ack & 0xff) ^ (ack >> 24);
      
              if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) ||
                  cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab))
                      return (NULL);
      
              mss = pf_syncookie_msstab[cookie.flags.mss_idx];
              wscale = pf_syncookie_wstab[cookie.flags.wscale_idx];
      
              return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport,
                  *pd->dport, seq, 0, TH_SYN, wscale, mss, pd->ttl, 0,
                  PF_TAG_SYNCOOKIE_RECREATED, cookie.flags.sack_ok, pd->rdomain));
      }
      /*        $OpenBSD: lpt.c,v 1.14 2015/05/11 02:01:01 guenther Exp $ */
      /*        $NetBSD: lpt.c,v 1.42 1996/10/21 22:41:14 thorpej Exp $        */
      
      /*
       * Copyright (c) 1993, 1994 Charles Hannum.
       * Copyright (c) 1990 William F. Jolitz, TeleMuse
       * 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. All advertising materials mentioning features or use of this software
       *    must display the following acknowledgement:
       *        This software is a component of "386BSD" developed by 
       *        William F. Jolitz, TeleMuse.
       * 4. Neither the name of the developer nor the name "386BSD"
       *    may be used to endorse or promote products derived from this software
       *    without specific prior written permission.
       *
       * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ 
       * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS 
       * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. 
       * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT 
       * NOT MAKE USE OF THIS WORK.
       *
       * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
       * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN 
       * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES 
       * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING 
       * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND 
       * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE 
       * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS 
       * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
       *
       * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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.
       */
      
      /*
       * Device Driver for AT parallel printer port
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/buf.h>
      #include <sys/kernel.h>
      #include <sys/uio.h>
      #include <sys/device.h>
      #include <sys/conf.h>
      #include <sys/syslog.h>
      
      #include <machine/bus.h>
      #include <machine/intr.h>
      
      #include <dev/ic/lptreg.h>
      #include <dev/ic/lptvar.h>
      
      #include "lpt.h"
      
      #define        TIMEOUT                hz*16        /* wait up to 16 seconds for a ready */
      #define        STEP                hz/4
      
      #define        LPTPRI                (PZERO+8)
      #define        LPT_BSIZE        1024
      
      #if !defined(DEBUG) || !defined(notdef)
      #define LPRINTF(a)
      #else
      #define LPRINTF(a)        if (lptdebug) printf a
      int lptdebug = 1;
      #endif
      
      /* XXX does not belong here */
      cdev_decl(lpt);
      
      struct cfdriver lpt_cd = {
              NULL, "lpt", DV_TTY
      };
      
      #define        LPTUNIT(s)        (minor(s) & 0x1f)
      #define        LPTFLAGS(s)        (minor(s) & 0xe0)
      
      #define        LPS_INVERT        (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK)
      #define        LPS_MASK        (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER)
      #define        NOT_READY() \
          ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK)
      #define        NOT_READY_ERR() \
          lpt_not_ready(bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status), sc)
      
      int        lpt_not_ready(u_int8_t, struct lpt_softc *);
      void        lptwakeup(void *arg);
      int        lptpushbytes(struct lpt_softc *);
      
      /*
       * Internal routine to lptprobe to do port tests of one byte value.
       */
      int
      lpt_port_test(bus_space_tag_t iot, bus_space_handle_t ioh, bus_addr_t base,
          bus_size_t off, u_int8_t data, u_int8_t mask)
      {
              int timeout;
              u_int8_t temp;
      
              data &= mask;
              bus_space_write_1(iot, ioh, off, data);
              timeout = 1000;
              do {
                      delay(10);
                      temp = bus_space_read_1(iot, ioh, off) & mask;
              } while (temp != data && --timeout);
              LPRINTF(("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off,
                  data, temp, timeout));
              return (temp == data);
      }
      
      void
      lpt_attach_common(struct lpt_softc *sc)
      {
              printf("\n");
      
              bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
      
              timeout_set(&sc->sc_wakeup_tmo, lptwakeup, sc);
      }
      
      void
      lpt_detach_common(struct lpt_softc *sc)
      {
              timeout_del(&sc->sc_wakeup_tmo);
              if (sc->sc_state != 0) {
                      sc->sc_state = 0;
                      wakeup(sc);
              }
      }
      
      /*
       * Reset the printer, then wait until it's selected and not busy.
       */
      int
      lptopen(dev_t dev, int flag, int mode, struct proc *p)
    2 {
              int unit = LPTUNIT(dev);
              u_int8_t flags = LPTFLAGS(dev);
              struct lpt_softc *sc;
              u_int8_t control;
              int error;
              int spin;
      
    1         if (unit >= lpt_cd.cd_ndevs)
                      return ENXIO;
              sc = lpt_cd.cd_devs[unit];
    1         if (!sc)
                      return ENXIO;
      
              sc->sc_flags = (sc->sc_flags & LPT_POLLED) | flags;
              if ((sc->sc_flags & (LPT_POLLED|LPT_NOINTR)) == LPT_POLLED)
                      return ENXIO;
      
      #ifdef DIAGNOSTIC
              if (sc->sc_state)
                      printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
                          sc->sc_state);
      #endif
      
              if (sc->sc_state)
                      return EBUSY;
      
              sc->sc_state = LPT_INIT;
              LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags));
      
              if ((flags & LPT_NOPRIME) == 0) {
                      /* assert INIT for 100 usec to start up printer */
                      bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_SELECT);
                      delay(100);
              }
      
              control = LPC_SELECT | LPC_NINIT;
              bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
      
              /* wait till ready (printer running diagnostics) */
              for (spin = 0; NOT_READY_ERR(); spin += STEP) {
                      if (spin >= TIMEOUT) {
                              sc->sc_state = 0;
                              return EBUSY;
                      }
      
                      /* wait 1/4 second, give up if we get a signal */
                      error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen", STEP);
                      if (sc->sc_state == 0)
                              return (EIO);
                      if (error != EWOULDBLOCK) {
                              sc->sc_state = 0;
                              return error;
                      }
              }
      
              if ((flags & LPT_NOINTR) == 0)
                      control |= LPC_IENABLE;
              if (flags & LPT_AUTOLF)
                      control |= LPC_AUTOLF;
              sc->sc_control = control;
              bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
      
              sc->sc_inbuf = geteblk(LPT_BSIZE);
              sc->sc_count = 0;
              sc->sc_state = LPT_OPEN;
      
              if ((sc->sc_flags & LPT_NOINTR) == 0)
                      lptwakeup(sc);
      
              LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname));
              return 0;
      }
      
      int
      lpt_not_ready(u_int8_t status, struct lpt_softc *sc)
      {
              u_int8_t new;
      
              status = (status ^ LPS_INVERT) & LPS_MASK;
              new = status & ~sc->sc_laststatus;
              sc->sc_laststatus = status;
      
              if (new & LPS_SELECT)
                      log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
              else if (new & LPS_NOPAPER)
                      log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
              else if (new & LPS_NERR)
                      log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
      
              return status;
      }
      
      void
      lptwakeup(void *arg)
      {
              struct lpt_softc *sc = arg;
              int s;
      
              s = spltty();
              lptintr(sc);
              splx(s);
      
              if (sc->sc_state != 0)
                      timeout_add(&sc->sc_wakeup_tmo, STEP);
      }
      
      /*
       * Close the device, and free the local line buffer.
       */
      int
      lptclose(dev_t dev, int flag, int mode, struct proc *p)
      {
              int unit = LPTUNIT(dev);
              struct lpt_softc *sc = lpt_cd.cd_devs[unit];
              bus_space_tag_t iot = sc->sc_iot;
              bus_space_handle_t ioh = sc->sc_ioh;
      
              if (sc->sc_count)
                      (void) lptpushbytes(sc);
      
              if ((sc->sc_flags & LPT_NOINTR) == 0)
                      timeout_del(&sc->sc_wakeup_tmo);
      
              bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
              sc->sc_state = 0;
              bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
              brelse(sc->sc_inbuf);
      
              LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname));
              return 0;
      }
      
      int
      lptpushbytes(struct lpt_softc *sc)
      {
              bus_space_tag_t iot = sc->sc_iot;
              bus_space_handle_t ioh = sc->sc_ioh;
              int error;
      
              if (sc->sc_flags & LPT_NOINTR) {
                      int spin, tic;
                      u_int8_t control = sc->sc_control;
      
                      while (sc->sc_count > 0) {
                              spin = 0;
                              if (sc->sc_state == 0)
                                      return (EIO);
                              while (NOT_READY()) {
                                      if (++spin < sc->sc_spinmax)
                                              continue;
                                      tic = 0;
                                      /* adapt busy-wait algorithm */
                                      sc->sc_spinmax++;
                                      while (NOT_READY_ERR()) {
                                              /* exponential backoff */
                                              tic = tic + tic + 1;
                                              if (tic > TIMEOUT)
                                                      tic = TIMEOUT;
                                              error = tsleep((caddr_t)sc,
                                                  LPTPRI | PCATCH, "lptpsh", tic);
                                              if (sc->sc_state == 0)
                                                      error = EIO;
                                              if (error != EWOULDBLOCK)
                                                      return error;
                                      }
                                      break;
                              }
      
                              bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
                              bus_space_write_1(iot, ioh, lpt_control,
                                  control | LPC_STROBE);
                              sc->sc_count--;
                              bus_space_write_1(iot, ioh, lpt_control, control);
      
                              /* adapt busy-wait algorithm */
                              if (spin*2 + 16 < sc->sc_spinmax)
                                      sc->sc_spinmax--;
                      }
              } else {
                      int s;
      
                      while (sc->sc_count > 0) {
                              /* if the printer is ready for a char, give it one */
                              if ((sc->sc_state & LPT_OBUSY) == 0) {
                                      LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname,
                                          sc->sc_count));
                                      s = spltty();
                                      (void) lptintr(sc);
                                      splx(s);
                              }
                              if (sc->sc_state == 0)
                                      return (EIO);
                              error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
                                  "lptwrite2", 0);
                              if (sc->sc_state == 0)
                                      error = EIO;
                              if (error)
                                      return error;
                      }
              }
              return 0;
      }
      
      /* 
       * Copy a line from user space to a local buffer, then call putc to get the
       * chars moved to the output queue.
       */
      int
      lptwrite(dev_t dev, struct uio *uio, int flags)
      {
              struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
              size_t n;
              int error = 0;
      
              while ((n = ulmin(LPT_BSIZE, uio->uio_resid)) != 0) {
                      error = uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
                      if (error != 0)
                              return error;
                      sc->sc_count = n;
                      error = lptpushbytes(sc);
                      if (error) {
                              /*
                               * Return accurate residual if interrupted or timed
                               * out.
                               */
                              uio->uio_resid += sc->sc_count;
                              sc->sc_count = 0;
                              return error;
                      }
              }
              return 0;
      }
      
      /*
       * Handle printer interrupts which occur when the printer is ready to accept
       * another char.
       */
      int
      lptintr(void *arg)
      {
              struct lpt_softc *sc = arg;
              bus_space_tag_t iot = sc->sc_iot;
              bus_space_handle_t ioh = sc->sc_ioh;
      
              if (((sc->sc_state & LPT_OPEN) == 0 && sc->sc_count == 0) ||
                  (sc->sc_flags & LPT_NOINTR))
                      return 0;
      
              /* is printer online and ready for output */
              if (NOT_READY() && NOT_READY_ERR())
                      return -1;
      
              if (sc->sc_count) {
                      u_int8_t control = sc->sc_control;
                      /* send char */
                      bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
                      delay (50);
                      bus_space_write_1(iot, ioh, lpt_control, control | LPC_STROBE);
                      sc->sc_count--;
                      bus_space_write_1(iot, ioh, lpt_control, control);
                      sc->sc_state |= LPT_OBUSY;
              } else
                      sc->sc_state &= ~LPT_OBUSY;
      
              if (sc->sc_count == 0) {
                      /* none, wake up the top half to get more */
                      wakeup((caddr_t)sc);
              }
      
              return 1;
      }
      
      int
      lpt_activate(struct device *self, int act)
      {
              struct lpt_softc *sc = (struct lpt_softc *)self;
      
              switch (act) {
              case DVACT_SUSPEND:
                      timeout_del(&sc->sc_wakeup_tmo);
                      break;
              case DVACT_RESUME:
                      bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
      
                      if (sc->sc_state) {
                              int spin;
      
                              if ((sc->sc_flags & LPT_NOPRIME) == 0) {
                                      /* assert INIT for 100 usec to start up printer */
                                      bus_space_write_1(sc->sc_iot, sc->sc_ioh,
                                          lpt_control, LPC_SELECT);
                                      delay(100);
                              }
                              
                              bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control,
                                  LPC_SELECT | LPC_NINIT);
      
                              /* wait till ready (printer running diagnostics) */
                              for (spin = 0; NOT_READY_ERR(); spin += STEP) {
                                      if (spin >= TIMEOUT) {
                                              sc->sc_state = 0;
                                              goto fail;
                                      }
      
                                      /* wait 1/4 second, give up if we get a signal */
                                      delay(STEP * 1000);
                              }
      
                              bus_space_write_1(sc->sc_iot, sc->sc_ioh,
                                  lpt_control, sc->sc_control);
                              wakeup(sc);
                      }
      fail:
                      break;
              }
       
              return (0);
      }
      /*        $OpenBSD: udp6_output.c,v 1.56 2018/09/13 19:53:58 bluhm Exp $        */
      /*        $KAME: udp6_output.c,v 1.21 2001/02/07 11:51:54 itojun 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, 1989, 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.
       */
      
      #include "pf.h"
      
      #include <sys/param.h>
      #include <sys/mbuf.h>
      #include <sys/protosw.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/errno.h>
      #include <sys/stat.h>
      #include <sys/systm.h>
      #include <sys/syslog.h>
      
      #include <net/if.h>
      #include <net/if_var.h>
      #include <net/route.h>
      #if NPF > 0
      #include <net/pfvar.h>
      #endif
      
      #include <netinet/in.h>
      #include <netinet6/in6_var.h>
      #include <netinet/ip.h>
      #include <netinet/ip_var.h>
      #include <netinet/in_pcb.h>
      #include <netinet/udp.h>
      #include <netinet/udp_var.h>
      #include <netinet/ip6.h>
      #include <netinet6/ip6_var.h>
      #include <netinet/icmp6.h>
      #include <netinet6/ip6protosw.h>
      
      /*
       * UDP protocol inplementation.
       * Per RFC 768, August, 1980.
       */
      int
      udp6_output(struct inpcb *in6p, struct mbuf *m, struct mbuf *addr6,
              struct mbuf *control)
   86 {
              u_int32_t ulen = m->m_pkthdr.len;
              u_int32_t plen = sizeof(struct udphdr) + ulen;
              int error = 0, priv = 0, hlen, flags;
              struct ip6_hdr *ip6;
   57         struct udphdr *udp6;
              struct in6_addr *laddr, *faddr;
   31         struct ip6_pktopts *optp, opt;
              struct sockaddr_in6 tmp, valid;
              struct proc *p = curproc;        /* XXX */
              u_short fport;
      
              if ((in6p->inp_socket->so_state & SS_PRIV) != 0)
                      priv = 1;
   55         if (control) {
   31                 if ((error = ip6_setpktopts(control, &opt,
                          in6p->inp_outputopts6, priv, IPPROTO_UDP)) != 0)
                              goto release;
                      optp = &opt;
              } else
                      optp = in6p->inp_outputopts6;
      
              if (addr6) {
                      struct sockaddr_in6 *sin6;
      
    4                 if ((error = in6_nam2sin6(addr6, &sin6)))
                              goto release;
    1                 if (sin6->sin6_port == 0) {
                              error = EADDRNOTAVAIL;
                              goto release;
                      }
   13                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
                              error = EADDRNOTAVAIL;
                              goto release;
                      }
    6                 if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6)) {
                              error = EISCONN;
                              goto release;
                      }
      
                      /* protect *sin6 from overwrites */
                      tmp = *sin6;
                      sin6 = &tmp;
      
                      faddr = &sin6->sin6_addr;
                      fport = sin6->sin6_port; /* allow 0 port */
      
                      /* KAME hack: embed scopeid */
    1                 if (in6_embedscope(&sin6->sin6_addr, sin6, in6p) != 0) {
                              error = EINVAL;
                              goto release;
                      }
      
                      error = in6_pcbselsrc(&laddr, sin6, in6p, optp);
    2                 if (error)
                              goto release;
      
    2                 if (in6p->inp_lport == 0){
                              error = in_pcbbind(in6p, NULL, p);
    2                         if (error)
                                      goto release;
                      }
      
    4                 if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_laddr6) &&
                          !IN6_ARE_ADDR_EQUAL(&in6p->inp_laddr6, laddr)) {
                              valid.sin6_addr = *laddr;
                              valid.sin6_port = in6p->inp_lport;
                              valid.sin6_scope_id = 0;
                              valid.sin6_family = AF_INET6;
                              valid.sin6_len = sizeof(valid);
                              error = in6_pcbaddrisavail(in6p, &valid, 0, p);
                              if (error)
                                      goto release;
                      }
              } else {
   55                 if (IN6_IS_ADDR_UNSPECIFIED(&in6p->inp_faddr6)) {
                              error = ENOTCONN;
                              goto release;
                      }
                      laddr = &in6p->inp_laddr6;
                      faddr = &in6p->inp_faddr6;
                      fport = in6p->inp_fport;
              }
      
              hlen = sizeof(struct ip6_hdr);
      
              /*
               * Calculate data length and get a mbuf
               * for UDP and IP6 headers.
               */
              M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT);
              if (m == NULL) {
                      error = ENOBUFS;
                      goto releaseopt;
              }
      
              /*
               * Stuff checksum and output datagram.
               */
              udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
              udp6->uh_sport = in6p->inp_lport; /* lport is always set in the PCB */
              udp6->uh_dport = fport;
              if (plen <= 0xffff)
                      udp6->uh_ulen = htons((u_short)plen);
              else
                      udp6->uh_ulen = 0;
              udp6->uh_sum = 0;
      
              ip6 = mtod(m, struct ip6_hdr *);
              ip6->ip6_flow        = in6p->inp_flowinfo & IPV6_FLOWINFO_MASK;
              ip6->ip6_vfc        &= ~IPV6_VERSION_MASK;
              ip6->ip6_vfc        |= IPV6_VERSION;
      #if 0        /* ip6_plen will be filled in ip6_output. */
              ip6->ip6_plen        = htons((u_short)plen);
      #endif
              ip6->ip6_nxt        = IPPROTO_UDP;
              ip6->ip6_hlim        = in6_selecthlim(in6p);
              ip6->ip6_src        = *laddr;
              ip6->ip6_dst        = *faddr;
      
              m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
      
              flags = 0;
              if (in6p->inp_flags & IN6P_MINMTU)
                      flags |= IPV6_MINMTU;
      
              udpstat_inc(udps_opackets);
      
              /* force routing table */
              m->m_pkthdr.ph_rtableid = in6p->inp_rtableid;
      
      #if NPF > 0
    3         if (in6p->inp_socket->so_state & SS_ISCONNECTED)
   54                 pf_mbuf_link_inpcb(m, in6p);
      #endif
      
              error = ip6_output(m, optp, &in6p->inp_route6,
                  flags, in6p->inp_moptions6, in6p);
              goto releaseopt;
      
      release:
              m_freem(m);
      
      releaseopt:
   55         if (control) {
                      ip6_clearpktopts(&opt, -1);
                      m_freem(control);
              }
              return (error);
      }
      /*        $OpenBSD: clock.c,v 1.30 2019/07/19 14:50:43 cheloha Exp $        */
      /*        $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $        */
      
      /*-
       * Copyright (c) 1993, 1994 Charles M. Hannum.
       * Copyright (c) 1990 The Regents of the University of California.
       * All rights reserved.
       *
       * This code is derived from software contributed to Berkeley by
       * William Jolitz and Don Ahn.
       *
       * 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.
       *
       *        @(#)clock.c        7.2 (Berkeley) 5/12/91
       */
      /*
       * Mach Operating System
       * Copyright (c) 1991,1990,1989 Carnegie Mellon University
       * All Rights Reserved.
       *
       * Permission to use, copy, modify and distribute this software and its
       * documentation is hereby granted, provided that both the copyright
       * notice and this permission notice appear in all copies of the
       * software, derivative works or modified versions, and any portions
       * thereof, and that both notices appear in supporting documentation.
       *
       * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
       * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
       * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
       *
       * Carnegie Mellon requests users of this software to return to
       *
       *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
       *  School of Computer Science
       *  Carnegie Mellon University
       *  Pittsburgh PA 15213-3890
       *
       * any improvements or extensions that they make and grant Carnegie Mellon
       * the rights to redistribute these changes.
       */
      /*
        Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
      
                      All Rights Reserved
      
      Permission to use, copy, modify, and distribute this software and
      its documentation for any purpose and without fee is hereby
      granted, provided that the above copyright notice appears in all
      copies and that both the copyright notice and this permission notice
      appear in supporting documentation, and that the name of Intel
      not be used in advertising or publicity pertaining to distribution
      of the software without specific, written prior permission.
      
      INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
      INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
      IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
      CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
      NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
      WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      */
      
      /*
       * Primitive clock interrupt routines.
       */
      
      /* #define CLOCKDEBUG */
      /* #define CLOCK_PARANOIA */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/time.h>
      #include <sys/kernel.h>
      #include <sys/timeout.h>
      #include <sys/timetc.h>
      
      #include <machine/cpu.h>
      #include <machine/intr.h>
      #include <machine/pio.h>
      #include <machine/cpufunc.h>
      
      #include <dev/isa/isareg.h>
      #include <dev/isa/isavar.h>
      #include <dev/ic/mc146818reg.h>
      #include <dev/ic/i8253reg.h>
      #include <amd64/isa/nvram.h>
      
      /* Timecounter on the i8254 */
      u_int32_t i8254_lastcount;
      u_int32_t i8254_offset;
      int i8254_ticked;
      u_int i8254_get_timecount(struct timecounter *tc);
      
      u_int i8254_simple_get_timecount(struct timecounter *tc);
      
      static struct timecounter i8254_timecounter = {
              i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
      };
      
      int        clockintr(void *);
      int        rtcintr(void *);
      int        gettick(void);
      void        rtcdrain(void *v);
      int        rtcget(mc_todregs *);
      void        rtcput(mc_todregs *);
      int        bcdtobin(int);
      int        bintobcd(int);
      
      u_int mc146818_read(void *, u_int);
      void mc146818_write(void *, u_int, u_int);
      
      u_int
   14 mc146818_read(void *sc, u_int reg)
      {
              outb(IO_RTC, reg);
              DELAY(1);
              return (inb(IO_RTC+1));
      }
      
      void
      mc146818_write(void *sc, u_int reg, u_int datum)
      {
              outb(IO_RTC, reg);
              DELAY(1);
              outb(IO_RTC+1, datum);
              DELAY(1);
      }
      
      struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
      
      u_long rtclock_tval;
      
      void
      startclocks(void)
      {
              mtx_enter(&timer_mutex);
              rtclock_tval = TIMER_DIV(hz);
              i8254_startclock();
              mtx_leave(&timer_mutex);
      }
      
      int
      clockintr(void *arg)
      {
              struct clockframe *frame = arg;
      
              if (timecounter->tc_get_timecount == i8254_get_timecount) {
                      if (i8254_ticked) {
                              i8254_ticked = 0;
                      } else {
                              i8254_offset += rtclock_tval;
                              i8254_lastcount = 0;
                      }
              }
      
              hardclock(frame);
      
              return 1;
      }
      
      int
      rtcintr(void *arg)
      {
              struct clockframe *frame = arg;
              u_int stat = 0;
      
              /*
               * If rtcintr is 'late', next intr may happen immediately.
               * Get them all. (Also, see comment in cpu_initclocks().)
               */
              while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
                      statclock(frame);
                      stat = 1;
              }
      
              return (stat);
      }
      
      int
      gettick(void)
      {
              u_long s;
              u_char lo, hi;
      
              /* Don't want someone screwing with the counter while we're here. */
              mtx_enter(&timer_mutex);
              s = intr_disable();
              /* Select counter 0 and latch it. */
              outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
              lo = inb(IO_TIMER1+TIMER_CNTR0);
              hi = inb(IO_TIMER1+TIMER_CNTR0);
              intr_restore(s);
              mtx_leave(&timer_mutex);
              return ((hi << 8) | lo);
      }
      
      /*
       * Wait "n" microseconds.
       * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
       * Note: timer had better have been programmed before this is first used!
       * (Note that we use `rate generator' mode, which counts at 1:1; `square
       * wave' mode counts at 2:1).
       */
      void
      i8254_delay(int n)
      {
              int limit, tick, otick;
              static const int delaytab[26] = {
                       0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
                      12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
                      24, 25, 27, 28, 29, 30,
              };
      
              /*
               * Read the counter first, so that the rest of the setup overhead is
               * counted.
               */
              otick = gettick();
      
              if (n <= 25)
                      n = delaytab[n];
              else {
                      /* Force 64-bit math to avoid 32-bit overflow if possible. */
                      n = (int64_t)n * TIMER_FREQ / 1000000;
              }
      
              limit = TIMER_FREQ / hz;
      
              while (n > 0) {
                      tick = gettick();
                      if (tick > otick)
                              n -= limit - (tick - otick);
                      else
                              n -= otick - tick;
                      otick = tick;
              }
      }
      
      void
      rtcdrain(void *v)
      {
              struct timeout *to = (struct timeout *)v;
      
              if (to != NULL)
                      timeout_del(to);
      
              /* Drain any un-acknowledged RTC interrupts. */
              while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
                      ; /* Nothing. */
      }
      
      void
      i8254_initclocks(void)
      {
              stathz = 128;
              profhz = 1024;
      
              /*
               * While the clock interrupt handler isn't really MPSAFE, the
               * i8254 can't really be used as a clock on a true MP system.
               */
              isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
                  clockintr, 0, "clock");
              isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
                  rtcintr, 0, "rtc");
      
              rtcstart();                        /* start the mc146818 clock */
      
              i8254_inittimecounter();        /* hook the interrupt-based i8254 tc */
      }
      
      void
      rtcstart(void)
      {
              static struct timeout rtcdrain_timeout;
      
              mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
              mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
      
              /*
               * On a number of i386 systems, the rtc will fail to start when booting
               * the system. This is due to us missing to acknowledge an interrupt
               * during early stages of the boot process. If we do not acknowledge
               * the interrupt, the rtc clock will not generate further interrupts.
               * To solve this, once interrupts are enabled, use a timeout (once)
               * to drain any un-acknowledged rtc interrupt(s).
               */
              timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
              timeout_add(&rtcdrain_timeout, 1);
      }
      
      void
      rtcstop(void)
      {
              mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
      }
      
      int
      rtcget(mc_todregs *regs)
   14 {
              if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
                      return (-1);
   14         MC146818_GETTOD(NULL, regs);                        /* XXX softc */
              return (0);
      }
      
      void
      rtcput(mc_todregs *regs)
   14 {
   14         MC146818_PUTTOD(NULL, regs);                        /* XXX softc */
      }
      
      int
      bcdtobin(int n)
      {
              return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
      }
      
      int
      bintobcd(int n)
      {
              return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
      }
      
      static int timeset;
      
      /*
       * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
       * to be called at splclock()
       */
      static int cmoscheck(void);
      static int
      cmoscheck(void)
      {
              int i;
              unsigned short cksum = 0;
      
              for (i = 0x10; i <= 0x2d; i++)
                      cksum += mc146818_read(NULL, i); /* XXX softc */
      
              return (cksum == (mc146818_read(NULL, 0x2e) << 8)
                                + mc146818_read(NULL, 0x2f));
      }
      
      /*
       * patchable to control century byte handling:
       * 1: always update
       * -1: never touch
       * 0: try to figure out itself
       */
      int rtc_update_century = 0;
      
      /*
       * Expand a two-digit year as read from the clock chip
       * into full width.
       * Being here, deal with the CMOS century byte.
       */
      static int centb = NVRAM_CENTURY;
      static int clock_expandyear(int);
      static int
      clock_expandyear(int clockyear)
      {
              int s, clockcentury, cmoscentury;
      
              clockcentury = (clockyear < 70) ? 20 : 19;
              clockyear += 100 * clockcentury;
      
              if (rtc_update_century < 0)
                      return (clockyear);
      
              s = splclock();
              if (cmoscheck())
                      cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
              else
                      cmoscentury = 0;
              splx(s);
              if (!cmoscentury)
                      return (clockyear);
      
              cmoscentury = bcdtobin(cmoscentury);
      
              if (cmoscentury != clockcentury) {
                      /* XXX note: saying "century is 20" might confuse the naive. */
                      printf("WARNING: NVRAM century is %d but RTC year is %d\n",
                             cmoscentury, clockyear);
      
                      /* Kludge to roll over century. */
                      if ((rtc_update_century > 0) ||
                          ((cmoscentury == 19) && (clockcentury == 20) &&
                           (clockyear == 2000))) {
                              printf("WARNING: Setting NVRAM century to %d\n",
                                     clockcentury);
                              s = splclock();
                              mc146818_write(NULL, centb, bintobcd(clockcentury));
                              splx(s);
                      }
              } else if (cmoscentury == 19 && rtc_update_century == 0)
                      rtc_update_century = 1; /* will update later in resettodr() */
      
              return (clockyear);
      }
      
      /*
       * Initialize the time of day register, based on the time base which is, e.g.
       * from a filesystem.
       */
      void
      inittodr(time_t base)
      {
              struct timespec ts;
              mc_todregs rtclk;
              struct clock_ymdhms dt;
              int s;
      
              ts.tv_nsec = 0;
      
              /*
               * We mostly ignore the suggested time (which comes from the
               * file system) and go for the RTC clock time stored in the
               * CMOS RAM.  If the time can't be obtained from the CMOS, or
               * if the time obtained from the CMOS is 5 or more years less
               * than the suggested time, we used the suggested time.  (In
               * the latter case, it's likely that the CMOS battery has
               * died.)
               */
      
              /*
               * if the file system time is more than a year older than the
               * kernel, warn and then set the base time to the CONFIG_TIME.
               */
              if (base < 30*SECYR) {        /* if before 2000, something's odd... */
                      printf("WARNING: preposterous time in file system\n");
                      base = 30*SECYR;
              }
      
              s = splclock();
              if (rtcget(&rtclk)) {
                      splx(s);
                      printf("WARNING: invalid time in clock chip\n");
                      goto fstime;
              }
              splx(s);
      #ifdef DEBUG_CLOCK
              printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
                  rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
                  rtclk[MC_SEC]);
      #endif
      
              dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
              dt.dt_min = bcdtobin(rtclk[MC_MIN]);
              dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
              dt.dt_day = bcdtobin(rtclk[MC_DOM]);
              dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
              dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
      
              ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
              if (tz.tz_dsttime)
                      ts.tv_sec -= 3600;
      
              if (base != 0 && base < ts.tv_sec - 5*SECYR)
                      printf("WARNING: file system time much less than clock time\n");
              else if (base > ts.tv_sec + 5*SECYR) {
                      printf("WARNING: clock time much less than file system time\n");
                      printf("WARNING: using file system time\n");
                      goto fstime;
              }
      
              tc_setclock(&ts);
              timeset = 1;
              return;
      
      fstime:
              ts.tv_sec = base;
              tc_setclock(&ts);
              timeset = 1;
              printf("WARNING: CHECK AND RESET THE DATE!\n");
      }
      
      /*
       * Reset the clock.
       */
      void
      resettodr(void)
   14 {
              mc_todregs rtclk;
              struct clock_ymdhms dt;
              int century, diff, s;
      
              /*
               * We might have been called by boot() due to a crash early
               * on.  Don't reset the clock chip in this case.
               */
              if (!timeset)
                      return;
      
              s = splclock();
   14         if (rtcget(&rtclk))
                      memset(&rtclk, 0, sizeof(rtclk));
              splx(s);
      
              diff = tz.tz_minuteswest * 60;
              if (tz.tz_dsttime)
                      diff -= 3600;
              clock_secs_to_ymdhms(time_second - diff, &dt);
      
              rtclk[MC_SEC] = bintobcd(dt.dt_sec);
              rtclk[MC_MIN] = bintobcd(dt.dt_min);
              rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
              rtclk[MC_DOW] = dt.dt_wday + 1;
              rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
              rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
              rtclk[MC_DOM] = bintobcd(dt.dt_day);
      
      #ifdef DEBUG_CLOCK
              printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
                 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
      #endif
              s = splclock();
              rtcput(&rtclk);
   14         if (rtc_update_century > 0) {
                      century = bintobcd(dt.dt_year / 100);
                      mc146818_write(NULL, centb, century); /* XXX softc */
              }
              splx(s);
      }
      
      void
      setstatclockrate(int arg)
      {
              if (initclock_func == i8254_initclocks) {
                      if (arg == stathz)
                              mc146818_write(NULL, MC_REGA,
                                  MC_BASE_32_KHz | MC_RATE_128_Hz);
                      else
                              mc146818_write(NULL, MC_REGA,
                                  MC_BASE_32_KHz | MC_RATE_1024_Hz);
              }
      }
      
      void
      i8254_inittimecounter(void)
      {
              tc_init(&i8254_timecounter);
      }
      
      /*
       * If we're using lapic to drive hardclock, we can use a simpler
       * algorithm for the i8254 timecounters.
       */
      void
      i8254_inittimecounter_simple(void)
      {
              i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
              i8254_timecounter.tc_counter_mask = 0x7fff;
              i8254_timecounter.tc_frequency = TIMER_FREQ;
      
              mtx_enter(&timer_mutex);
              rtclock_tval = 0x8000;
              i8254_startclock();
              mtx_leave(&timer_mutex);
      
              tc_init(&i8254_timecounter);
      }
      
      void
      i8254_startclock(void)
      {
              u_long tval = rtclock_tval;
      
              outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
              outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
              outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
      }
      
      u_int
      i8254_simple_get_timecount(struct timecounter *tc)
      {
              return (rtclock_tval - gettick());
      }
      
      u_int
      i8254_get_timecount(struct timecounter *tc)
      {
              u_char hi, lo;
              u_int count;
              u_long s;
      
              s = intr_disable();
      
              outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
              lo = inb(IO_TIMER1+TIMER_CNTR0);
              hi = inb(IO_TIMER1+TIMER_CNTR0);
      
              count = rtclock_tval - ((hi << 8) | lo);
      
              if (count < i8254_lastcount) {
                      i8254_ticked = 1;
                      i8254_offset += rtclock_tval;
              }
              i8254_lastcount = count;
              count += i8254_offset;
      
              intr_restore(s);
      
              return (count);
      }
      /*        $OpenBSD: subr_prf.c,v 1.99 2019/07/20 23:06:51 mpi Exp $        */
      /*        $NetBSD: subr_prf.c,v 1.45 1997/10/24 18:14:25 chuck Exp $        */
      
      /*-
       * Copyright (c) 1986, 1988, 1991, 1993
       *        The Regents of the University of California.  All rights reserved.
       * (c) UNIX System Laboratories, Inc.
       * All or some portions of this file are derived from material licensed
       * to the University of California by American Telephone and Telegraph
       * Co. or Unix System Laboratories, Inc. and are reproduced herein with
       * the permission of UNIX System Laboratories, Inc.
       *
       * 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.
       *
       *        @(#)subr_prf.c        8.3 (Berkeley) 1/21/94
       */
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/conf.h>
      #include <sys/reboot.h>
      #include <sys/msgbuf.h>
      #include <sys/proc.h>
      #include <sys/ioctl.h>
      #include <sys/vnode.h>
      #include <sys/tty.h>
      #include <sys/tprintf.h>
      #include <sys/syslog.h>
      #include <sys/malloc.h>
      #include <sys/pool.h>
      #include <sys/mutex.h>
      
      #include <dev/cons.h>
      
      /*
       * note that stdarg.h and the ansi style va_start macro is used for both
       * ansi and traditional c compilers.
       */
      #include <sys/stdarg.h>
      
      #ifdef DDB
      #include <ddb/db_output.h>        /* db_printf, db_putchar prototypes */
      #include <ddb/db_var.h>                /* db_log, db_radix */
      #endif
      
      
      /*
       * defines
       */
      
      /* flags for kprintf */
      #define TOCONS                0x01        /* to the console */
      #define TOTTY                0x02        /* to the process' tty */
      #define TOLOG                0x04        /* to the kernel message buffer */
      #define TOBUFONLY        0x08        /* to the buffer (only) [for snprintf] */
      #define TODDB                0x10        /* to ddb console */
      #define TOCOUNT                0x20        /* act like [v]snprintf */
      
      /* max size buffer kprintf needs to print quad_t [size in base 8 + \0] */
      #define KPRINTF_BUFSIZE                (sizeof(quad_t) * NBBY / 3 + 2)
      
      
      /*
       * local prototypes
       */
      
      int         kprintf(const char *, int, void *, char *, va_list);
      void         kputchar(int, int, struct tty *);
      
      struct mutex kprintf_mutex =
          MUTEX_INITIALIZER_FLAGS(IPL_HIGH, "kprintf", MTX_NOWITNESS);
      
      /*
       * globals
       */
      
      extern        int log_open;        /* subr_log: is /dev/klog open? */
      const        char *panicstr; /* arg to first call to panic (used as a flag
                                 to indicate that panic has already been called). */
      const        char *faultstr; /* page fault string */
      #ifdef DDB
      /*
       * Enter ddb on panic.
       */
      int        db_panic = 1;
      
      /*
       * db_console controls if we can be able to enter ddb by a special key
       * combination (machine dependent).
       * If DDB_SAFE_CONSOLE is defined in the kernel configuration it allows
       * to break into console during boot. It's _really_ useful when debugging
       * some things in the kernel that can cause init(8) to crash.
       */
      #ifdef DDB_SAFE_CONSOLE
      int        db_console = 1;
      #else
      int        db_console = 0;
      #endif
      #endif
      
      /*
       * panic on spl assertion failure?
       */
      #ifdef SPLASSERT_WATCH
      int splassert_ctl = 3;
      #else
      int splassert_ctl = 1;
      #endif
      
      /*
       * v_putc: routine to putc on virtual console
       *
       * the v_putc pointer can be used to redirect the console cnputc elsewhere
       * [e.g. to a "virtual console"].
       */
      
      void (*v_putc)(int) = cnputc;        /* start with cnputc (normal cons) */
      
      
      /*
       * functions
       */
      
      /*
       *        Partial support (the failure case) of the assertion facility
       *        commonly found in userland.
       */
      void
      __assert(const char *t, const char *f, int l, const char *e)
      {
      
              panic(__KASSERTSTR, t, e, f, l);
      }
      
      /*
       * tablefull: warn that a system table is full
       */
      
      void
      tablefull(const char *tab)
    2 {
              log(LOG_ERR, "%s: table is full\n", tab);
      }
      
      /*
       * panic: handle an unresolvable fatal error
       *
       * prints "panic: <message>" and reboots.   if called twice (i.e. recursive
       * call) we avoid trying to sync the disk and just reboot (to avoid
       * recursive panics).
       */
      
      void
      panic(const char *fmt, ...)
      {
              static char panicbuf[512];
              int bootopt;
              va_list ap;
      
              /* do not trigger assertions, we know that we are inconsistent */
              splassert_ctl = 0;
      
              bootopt = RB_AUTOBOOT | RB_DUMP;
              va_start(ap, fmt);
              if (panicstr)
                      bootopt |= RB_NOSYNC;
              else {
                      vsnprintf(panicbuf, sizeof panicbuf, fmt, ap);
                      panicstr = panicbuf;
              }
              va_end(ap);
      
              printf("panic: ");
              va_start(ap, fmt);
              vprintf(fmt, ap);
              printf("\n");
              va_end(ap);
      
      #ifdef DDB
              if (db_panic)
                      db_enter();
              else
                      db_stack_dump();
      #endif
              reboot(bootopt);
              /* NOTREACHED */
      }
      
      /*
       * We print only the function name. The file name is usually very long and
       * would eat tons of space in the kernel.
       */
      void
      splassert_fail(int wantipl, int haveipl, const char *func)
      {
              if (panicstr || db_active)
                      return;
      
              printf("splassert: %s: want %d have %d\n", func, wantipl, haveipl);
              switch (splassert_ctl) {
              case 1:
                      break;
              case 2:
      #ifdef DDB
                      db_stack_dump();
      #endif
                      break;
              case 3:
      #ifdef DDB
                      db_stack_dump();
                      db_enter();
      #endif
                      break;
              default:
                      panic("spl assertion failure in %s", func);
              }
      }
      
      /*
       * kernel logging functions: log, logpri, addlog
       */
      
      /*
       * log: write to the log buffer
       *
       * => will not sleep [so safe to call from interrupt]
       * => will log to console if /dev/klog isn't open
       */
      
      void
      log(int level, const char *fmt, ...)
    8 {
              int s;
              va_list ap;
      
              s = splhigh();
    8         logpri(level);                /* log the level first */
              va_start(ap, fmt);
              kprintf(fmt, TOLOG, NULL, NULL, ap);
              va_end(ap);
              splx(s);
    8         if (!log_open) {
                      va_start(ap, fmt);
                      mtx_enter(&kprintf_mutex);
                      kprintf(fmt, TOCONS, NULL, NULL, ap);
                      mtx_leave(&kprintf_mutex);
                      va_end(ap);
              }
              logwakeup();                /* wake up anyone waiting for log msgs */
      }
      
      /*
       * logpri: log the priority level to the klog
       */
      
      void
      logpri(int level)
      {
              char *p;
              char snbuf[KPRINTF_BUFSIZE];
      
              kputchar('<', TOLOG, NULL);
              snprintf(snbuf, sizeof snbuf, "%d", level);
    8         for (p = snbuf ; *p ; p++)
                      kputchar(*p, TOLOG, NULL);
              kputchar('>', TOLOG, NULL);
      }
      
      /*
       * addlog: add info to previous log message
       */
      
      int
      addlog(const char *fmt, ...)
      {
              int s;
              va_list ap;
      
              s = splhigh();
              va_start(ap, fmt);
              kprintf(fmt, TOLOG, NULL, NULL, ap);
              va_end(ap);
              splx(s);
              if (!log_open) {
                      va_start(ap, fmt);
                      mtx_enter(&kprintf_mutex);
                      kprintf(fmt, TOCONS, NULL, NULL, ap);
                      mtx_leave(&kprintf_mutex);
                      va_end(ap);
              }
              logwakeup();
              return(0);
      }
      
      
      /*
       * kputchar: print a single character on console or user terminal.
       *
       * => if console, then the last MSGBUFS chars are saved in msgbuf
       *        for inspection later (e.g. dmesg/syslog)
       */
      void
      kputchar(int c, int flags, struct tty *tp)
   50 {
              extern int msgbufmapped;
      
              if (panicstr)
                      constty = NULL;
      
   50         if ((flags & TOCONS) && tp == NULL && constty != NULL && !db_active) {
                      tp = constty;
                      flags |= TOTTY;
              }
   50         if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 &&
                  (flags & TOCONS) && tp == constty)
    1                 constty = NULL;
   28         if ((flags & TOLOG) &&
                  c != '\0' && c != '\r' && c != 0177 && msgbufmapped)
   22                 msgbuf_putchar(msgbufp, c);
   37         if ((flags & TOCONS) && (constty == NULL || db_active) && c != '\0')
   13                 (*v_putc)(c);
      #ifdef DDB
   50         if (flags & TODDB)
                      db_putchar(c);
      #endif
      }
      
      
      /*
       * uprintf: print to the controlling tty of the current process
       *
       * => we may block if the tty queue is full
       * => no message is printed if the queue doesn't clear in a reasonable
       *        time
       */
      
      void
      uprintf(const char *fmt, ...)
      {
              struct process *pr = curproc->p_p;
              va_list ap;
      
              if (pr->ps_flags & PS_CONTROLT && pr->ps_session->s_ttyvp) {
                      va_start(ap, fmt);
                      kprintf(fmt, TOTTY, pr->ps_session->s_ttyp, NULL, ap);
                      va_end(ap);
              }
      }
      
      #if defined(NFSSERVER) || defined(NFSCLIENT)
      
      /*
       * tprintf functions: used to send messages to a specific process
       *
       * usage:
       *   get a tpr_t handle on a process "p" by using "tprintf_open(p)"
       *   use the handle when calling "tprintf"
       *   when done, do a "tprintf_close" to drop the handle
       */
      
      /*
       * tprintf_open: get a tprintf handle on a process "p"
       * XXX change s/proc/process
       *
       * => returns NULL if process can't be printed to
       */
      
      tpr_t
      tprintf_open(struct proc *p)
      {
              struct process *pr = p->p_p;
      
              if (pr->ps_flags & PS_CONTROLT && pr->ps_session->s_ttyvp) {
                      SESSHOLD(pr->ps_session);
                      return ((tpr_t)pr->ps_session);
              }
              return ((tpr_t) NULL);
      }
      
      /*
       * tprintf_close: dispose of a tprintf handle obtained with tprintf_open
       */
      
      void
      tprintf_close(tpr_t sess)
      {
      
              if (sess)
                      SESSRELE((struct session *) sess);
      }
      
      /*
       * tprintf: given tprintf handle to a process [obtained with tprintf_open],
       * send a message to the controlling tty for that process.
       *
       * => also sends message to /dev/klog
       */
      void
      tprintf(tpr_t tpr, const char *fmt, ...)
      {
              struct session *sess = (struct session *)tpr;
              struct tty *tp = NULL;
              int flags = TOLOG;
              va_list ap;
      
              logpri(LOG_INFO);
              if (sess && sess->s_ttyvp && ttycheckoutq(sess->s_ttyp, 0)) {
                      flags |= TOTTY;
                      tp = sess->s_ttyp;
              }
              va_start(ap, fmt);
              kprintf(fmt, flags, tp, NULL, ap);
              va_end(ap);
              logwakeup();
      }
      
      #endif        /* NFSSERVER || NFSCLIENT */
      
      
      /*
       * ttyprintf: send a message to a specific tty
       *
       * => should be used only by tty driver or anything that knows the
       *        underlying tty will not be revoked(2)'d away.  [otherwise,
       *        use tprintf]
       */
      void
      ttyprintf(struct tty *tp, const char *fmt, ...)
   28 {
              va_list ap;
      
              va_start(ap, fmt);
              kprintf(fmt, TOTTY, tp, NULL, ap);
              va_end(ap);
      }
      
      #ifdef DDB
      
      /*
       * db_printf: printf for DDB (via db_putchar)
       */
      
      int
      db_printf(const char *fmt, ...)
      {
              va_list ap;
              int retval;
      
              va_start(ap, fmt);
              retval = db_vprintf(fmt, ap);
              va_end(ap);
              return(retval);
      }
      
      int
      db_vprintf(const char *fmt, va_list ap)
      {
              int flags;
      
              flags = TODDB;
              if (db_log)
                      flags |= TOLOG;
              return (kprintf(fmt, flags, NULL, NULL, ap));
      }
      #endif /* DDB */
      
      
      /*
       * normal kernel printf functions: printf, vprintf, snprintf
       */
      
      /*
       * printf: print a message to the console and the log
       */
      int
      printf(const char *fmt, ...)
   14 {
              va_list ap;
              int retval;
      
      
              va_start(ap, fmt);
              mtx_enter(&kprintf_mutex);
              retval = kprintf(fmt, TOCONS | TOLOG, NULL, NULL, ap);
              mtx_leave(&kprintf_mutex);
              va_end(ap);
              if (!panicstr)
   13                 logwakeup();
      
      
              return(retval);
      }
      
      /*
       * vprintf: print a message to the console and the log [already have a
       *        va_list]
       */
      
      int
      vprintf(const char *fmt, va_list ap)
      {
              int retval;
      
              mtx_enter(&kprintf_mutex);
              retval = kprintf(fmt, TOCONS | TOLOG, NULL, NULL, ap);
              mtx_leave(&kprintf_mutex);
              if (!panicstr)
                      logwakeup();
      
      
              return (retval);
      }
      
      /*
       * snprintf: print a message to a buffer
       */
      int
      snprintf(char *buf, size_t size, const char *fmt, ...)
   65 {
              int retval;
              va_list ap;
              char *p;
      
              p = buf + size - 1;
              if (size < 1)
                      p = buf;
              va_start(ap, fmt);
              retval = kprintf(fmt, TOBUFONLY | TOCOUNT, &p, buf, ap);
              va_end(ap);
              if (size > 0)
   65                 *(p) = 0;        /* null terminate */
              return(retval);
      }
      
      /*
       * vsnprintf: print a message to a buffer [already have va_alist]
       */
      int
      vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
      {
              int retval;
              char *p;
      
              p = buf + size - 1;
              if (size < 1)
                      p = buf;
              retval = kprintf(fmt, TOBUFONLY | TOCOUNT, &p, buf, ap);
              if (size > 0)
                      *(p) = 0;        /* null terminate */
              return(retval);
      }
      
      /*
       * kprintf: scaled down version of printf(3).
       *
       * this version based on vfprintf() from libc which was derived from
       * software contributed to Berkeley by Chris Torek.
       *
       * The additional format %b is supported to decode error registers.
       * Its usage is:
       *
       *        printf("reg=%b\n", regval, "<base><arg>*");
       *
       * where <base> is the output base expressed as a control character, e.g.
       * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
       * the first of which gives the bit number to be inspected (origin 1), and
       * the next characters (up to a control character, i.e. a character <= 32),
       * give the name of the register.  Thus:
       *
       *        kprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
       *
       * would produce output:
       *
       *        reg=3<BITTWO,BITONE>
       *
       * To support larger integers (> 32 bits), %b formatting will also accept
       * control characters in the region 0x80 - 0xff.  0x80 refers to bit 0,
       * 0x81 refers to bit 1, and so on.  The equivalent string to the above is:
       *
       *        kprintf("reg=%b\n", 3, "\10\201BITTWO\200BITONE\n");
       *
       * and would produce the same output.
       *
       * Like the rest of printf, %b can be prefixed to handle various size
       * modifiers, eg. %b is for "int", %lb is for "long", and %llb supports
       * "long long".
       *
       * This code is large and complicated...
       */
      
      /*
       * macros for converting digits to letters and vice versa
       */
      #define        to_digit(c)        ((c) - '0')
      #define is_digit(c)        ((unsigned)to_digit(c) <= 9)
      #define        to_char(n)        ((n) + '0')
      
      /*
       * flags used during conversion.
       */
      #define        ALT                0x001                /* alternate form */
      #define        HEXPREFIX        0x002                /* add 0x or 0X prefix */
      #define        LADJUST                0x004                /* left adjustment */
      #define        LONGDBL                0x008                /* long double; unimplemented */
      #define        LONGINT                0x010                /* long integer */
      #define        QUADINT                0x020                /* quad integer */
      #define        SHORTINT        0x040                /* short integer */
      #define        ZEROPAD                0x080                /* zero (as opposed to blank) pad */
      #define FPT                0x100                /* Floating point number */
      #define SIZEINT                0x200                /* (signed) size_t */
      
              /*
               * To extend shorts properly, we need both signed and unsigned
               * argument extraction methods.
               */
      #define        SARG() \
              (flags&QUADINT ? va_arg(ap, quad_t) : \
                  flags&LONGINT ? va_arg(ap, long) : \
                  flags&SIZEINT ? va_arg(ap, ssize_t) : \
                  flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
                  (long)va_arg(ap, int))
      #define        UARG() \
              (flags&QUADINT ? va_arg(ap, u_quad_t) : \
                  flags&LONGINT ? va_arg(ap, u_long) : \
                  flags&SIZEINT ? va_arg(ap, size_t) : \
                  flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
                  (u_long)va_arg(ap, u_int))
      
      #define KPRINTF_PUTCHAR(C) do {                                        \
              int chr = (C);                                                        \
              ret += 1;                                                        \
              if (oflags & TOBUFONLY) {                                        \
                      if ((vp != NULL) && (sbuf == tailp)) {                        \
                              if (!(oflags & TOCOUNT))                                \
                                      goto overflow;                                \
                      } else                                                        \
                              *sbuf++ = chr;                                        \
              } else {                                                        \
                      kputchar(chr, oflags, (struct tty *)vp);                        \
              }                                                                \
      } while(0)
      
      int
      kprintf(const char *fmt0, int oflags, void *vp, char *sbuf, va_list ap)
  107 {
              char *fmt;                /* format string */
              int ch;                        /* character from fmt */
              int n;                        /* handy integer (short term usage) */
              char *cp = NULL;        /* handy char pointer (short term usage) */
              int flags;                /* flags as above */
    2         int ret;                /* return value accumulator */
   35         int width;                /* width from format (%8d), or 0 */
              int prec;                /* precision from format (%.3d), or -1 */
              char sign;                /* sign prefix (' ', '+', '-', or \0) */
      
              u_quad_t _uquad;        /* integer arguments %[diouxX] */
              enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
              int dprec;                /* a copy of prec if [diouxX], 0 otherwise */
              int realsz;                /* field size expanded by dprec */
              int size = 0;                /* size of converted field or string */
              char *xdigs = NULL;        /* digits for [xX] conversion */
              char buf[KPRINTF_BUFSIZE]; /* space for %c, %[diouxX] */
              char *tailp = NULL;        /* tail pointer for snprintf */
      
   93         if (oflags & TOCONS)
   14                 MUTEX_ASSERT_LOCKED(&kprintf_mutex);
      
   50         if ((oflags & TOBUFONLY) && (vp != NULL))
   65                 tailp = *(char **)vp;
      
              fmt = (char *)fmt0;
              ret = 0;
      
              /*
               * Scan the format for conversions (`%' character).
               */
              for (;;) {
  106                 while (*fmt != '%' && *fmt) {
   79                         KPRINTF_PUTCHAR(*fmt++);
                      }
                      if (*fmt == 0)
                              goto done;
      
                      fmt++;                /* skip over '%' */
      
                      flags = 0;
                      dprec = 0;
                      width = 0;
                      prec = -1;
                      sign = '\0';
      
      rflag:                ch = *fmt++;
  100 reswitch:        switch (ch) {
                      /* XXX: non-standard '%b' format */
                      case 'b': {
                              char *b, *z;
                              int tmp;
                              _uquad = UARG();
                              b = va_arg(ap, char *);
                              if (*b == 8)
                                      snprintf(buf, sizeof buf, "%llo", _uquad);
                              else if (*b == 10)
                                      snprintf(buf, sizeof buf, "%lld", _uquad);
                              else if (*b == 16)
                                      snprintf(buf, sizeof buf, "%llx", _uquad);
                              else
                                      break;
                              b++;
      
                              z = buf;
                              while (*z) {
                                      KPRINTF_PUTCHAR(*z++);
                              }
      
                              if (_uquad) {
                                      tmp = 0;
                                      while ((n = *b++) != 0) {
                                              if (n & 0x80)
                                                      n &= 0x7f;
                                              else if (n <= ' ')
                                                      n = n - 1;
                                              if (_uquad & (1LL << n)) {
                                                      KPRINTF_PUTCHAR(tmp ? ',':'<');
                                                      while (*b > ' ' &&
                                                          (*b & 0x80) == 0) {
                                                              KPRINTF_PUTCHAR(*b);
                                                              b++;
                                                      }
                                                      tmp = 1;
                                              } else {
                                                      while (*b > ' ' &&
                                                          (*b & 0x80) == 0)
                                                              b++;
                                              }
                                      }
                                      if (tmp) {
                                              KPRINTF_PUTCHAR('>');
                                      }
                              }
                              continue;        /* no output */
                      }
      
                      case ' ':
                              /*
                               * ``If the space and + flags both appear, the space
                               * flag will be ignored.''
                               *        -- ANSI X3J11
                               */
                              if (!sign)
                                      sign = ' ';
                              goto rflag;
                      case '#':
                              flags |= ALT;
                              goto rflag;
                      case '*':
                              /*
                               * ``A negative field width argument is taken as a
                               * - flag followed by a positive field width.''
                               *        -- ANSI X3J11
                               * They don't exclude field widths read from args.
                               */
                              if ((width = va_arg(ap, int)) >= 0)
                                      goto rflag;
                              width = -width;
                              /* FALLTHROUGH */
                      case '-':
                              flags |= LADJUST;
                              goto rflag;
                      case '+':
                              sign = '+';
                              goto rflag;
                      case '.':
                              if ((ch = *fmt++) == '*') {
                                      n = va_arg(ap, int);
                                      prec = n < 0 ? -1 : n;
                                      goto rflag;
                              }
                              n = 0;
                              while (is_digit(ch)) {
                                      n = 10 * n + to_digit(ch);
                                      ch = *fmt++;
                              }
                              prec = n < 0 ? -1 : n;
                              goto reswitch;
                      case '0':
                              /*
                               * ``Note that 0 is taken as a flag, not as the
                               * beginning of a field width.''
                               *        -- ANSI X3J11
                               */
                              flags |= ZEROPAD;
                              goto rflag;
                      case '1': case '2': case '3': case '4':
                      case '5': case '6': case '7': case '8': case '9':
                              n = 0;
                              do {
                                      n = 10 * n + to_digit(ch);
   28                                 ch = *fmt++;
                              } while (is_digit(ch));
                              width = n;
                              goto reswitch;
                      case 'h':
                              flags |= SHORTINT;
                              goto rflag;
                      case 'l':
                              if (*fmt == 'l') {
                                      fmt++;
                                      flags |= QUADINT;
                              } else {
                                      flags |= LONGINT;
                              }
                              goto rflag;
                      case 'q':
                              flags |= QUADINT;
                              goto rflag;
                      case 'z':
                              flags |= SIZEINT;
                              goto rflag;
                      case 'c':
                              *(cp = buf) = va_arg(ap, int);
                              size = 1;
                              sign = '\0';
                              break;
                      case 't':
                              /* ptrdiff_t */
                              /* FALLTHROUGH */
                      case 'D':
                              flags |= LONGINT;
                              /*FALLTHROUGH*/
                      case 'd':
                      case 'i':
   95                         _uquad = SARG();
                              if ((quad_t)_uquad < 0) {
                                      _uquad = -_uquad;
                                      sign = '-';
                              }
                              base = DEC;
                              goto number;
                      case 'n':
                              /* %n is unsupported in the kernel; just skip it */
                              if (flags & QUADINT)
                                      (void)va_arg(ap, quad_t *);
                              else if (flags & LONGINT)
                                      (void)va_arg(ap, long *);
                              else if (flags & SHORTINT)
                                      (void)va_arg(ap, short *);
                              else if (flags & SIZEINT)
                                      (void)va_arg(ap, ssize_t *);
                              else
                                      (void)va_arg(ap, int *);
                              continue;        /* no output */
                      case 'O':
                              flags |= LONGINT;
                              /*FALLTHROUGH*/
                      case 'o':
                              _uquad = UARG();
                              base = OCT;
                              goto nosign;
                      case 'p':
                              /*
                               * ``The argument shall be a pointer to void.  The
                               * value of the pointer is converted to a sequence
                               * of printable characters, in an implementation-
                               * defined manner.''
                               *        -- ANSI X3J11
                               */
    5                         _uquad = (u_long)va_arg(ap, void *);
                              base = HEX;
                              xdigs = "0123456789abcdef";
                              flags |= HEXPREFIX;
                              ch = 'x';
                              goto nosign;
                      case 's':
   36                         if ((cp = va_arg(ap, char *)) == NULL)
                                      cp = "(null)";
                              if (prec >= 0) {
                                      /*
                                       * can't use strlen; can only look for the
                                       * NUL in the first `prec' characters, and
                                       * strlen() will go further.
                                       */
                                      char *p = memchr(cp, 0, prec);
      
                                      if (p != NULL) {
                                              size = p - cp;
                                              if (size > prec)
                                                      size = prec;
                                      } else
                                              size = prec;
                              } else
   36                                 size = strlen(cp);
                              sign = '\0';
                              break;
                      case 'U':
                              flags |= LONGINT;
                              /*FALLTHROUGH*/
                      case 'u':
                              _uquad = UARG();
                              base = DEC;
                              goto nosign;
                      case 'X':
                              xdigs = "0123456789ABCDEF";
                              goto hex;
                      case 'x':
                              xdigs = "0123456789abcdef";
    5 hex:                        _uquad = UARG();
                              base = HEX;
                              /* leading 0x/X only if non-zero */
                              if (flags & ALT && _uquad != 0)
                                      flags |= HEXPREFIX;
      
                              /* unsigned conversions */
      nosign:                        sign = '\0';
                              /*
                               * ``... diouXx conversions ... if a precision is
                               * specified, the 0 flag will be ignored.''
                               *        -- ANSI X3J11
                               */
      number:                        if ((dprec = prec) >= 0)
                                      flags &= ~ZEROPAD;
      
                              /*
                               * ``The result of converting a zero value with an
                               * explicit precision of zero is no characters.''
                               *        -- ANSI X3J11
                               */
                              cp = buf + KPRINTF_BUFSIZE;
                              if (_uquad != 0 || prec != 0) {
                                      /*
                                       * Unsigned mod is hard, and unsigned mod
                                       * by a constant is easier than that by
                                       * a variable; hence this switch.
                                       */
                                      switch (base) {
                                      case OCT:
                                              do {
                                                      *--cp = to_char(_uquad & 7);
                                                      _uquad >>= 3;
                                              } while (_uquad);
                                              /* handle octal leading 0 */
                                              if (flags & ALT && *cp != '0')
                                                      *--cp = '0';
                                              break;
      
                                      case DEC:
                                              /* many numbers are 1 digit */
   95                                         while (_uquad >= 10) {
                                                      *--cp = to_char(_uquad % 10);
                                                      _uquad /= 10;
                                              }
                                              *--cp = to_char(_uquad);
                                              break;
      
                                      case HEX:
                                              do {
                                                      *--cp = xdigs[_uquad & 15];
                                                      _uquad >>= 4;
    5                                         } while (_uquad);
                                              break;
      
                                      default:
                                              cp = "bug in kprintf: bad base";
                                              size = strlen(cp);
                                              goto skipsize;
                                      }
                              }
                              size = buf + KPRINTF_BUFSIZE - cp;
                      skipsize:
                              break;
                      default:        /* "%?" prints ?, unless ? is NUL */
                              if (ch == '\0')
                                      goto done;
                              /* pretend it was %c with argument ch */
                              cp = buf;
                              *cp = ch;
                              size = 1;
                              sign = '\0';
                              break;
                      }
      
                      /*
                       * All reasonable formats wind up here.  At this point, `cp'
                       * points to a string which (if not flags&LADJUST) should be
                       * padded out to `width' places.  If flags&ZEROPAD, it should
                       * first be prefixed by any sign or other prefix; otherwise,
                       * it should be blank padded before the prefix is emitted.
                       * After any left-hand padding and prefixing, emit zeroes
                       * required by a decimal [diouxX] precision, then print the
                       * string proper, then emit zeroes required by any leftover
                       * floating precision; finally, if LADJUST, pad with blanks.
                       *
                       * Compute actual size, so we know how much to pad.
                       * size excludes decimal prec; realsz includes it.
                       */
                      realsz = dprec > size ? dprec : size;
  100                 if (sign)
                              realsz++;
                      else if (flags & HEXPREFIX)
                              realsz+= 2;
      
                      /* right-adjusting blank padding */
   28                 if ((flags & (LADJUST|ZEROPAD)) == 0) {
                              n = width - realsz;
  101                         while (n-- > 0)
                                      KPRINTF_PUTCHAR(' ');
                      }
      
                      /* prefix */
                      if (sign) {
                              KPRINTF_PUTCHAR(sign);
  101                 } else if (flags & HEXPREFIX) {
    5                         KPRINTF_PUTCHAR('0');
                              KPRINTF_PUTCHAR(ch);
                      }
      
                      /* right-adjusting zero padding */
  101                 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) {
                              n = width - realsz;
   28                         while (n-- > 0)
    4                                 KPRINTF_PUTCHAR('0');
                      }
      
                      /* leading zeroes from decimal precision */
                      n = dprec - size;
  101                 while (n-- > 0)
                              KPRINTF_PUTCHAR('0');
      
                      /* the string or number proper */
  101                 while (size--)
  101                         KPRINTF_PUTCHAR(*cp++);
                      /* left-adjusting padding (always blank) */
  100                 if (flags & LADJUST) {
                              n = width - realsz;
                              while (n-- > 0)
                                      KPRINTF_PUTCHAR(' ');
                      }
              }
      
      done:
   49         if ((oflags & TOBUFONLY) && (vp != NULL))
   65                 *(char **)vp = sbuf;
      overflow:
              return (ret);
              /* NOTREACHED */
      }
      
      #if __GNUC_PREREQ__(2,96)
      /*
       * XXX - these functions shouldn't be in the kernel, but gcc 3.X feels like
       *       translating some printf calls to puts and since it doesn't seem
       *       possible to just turn off parts of those optimizations (some of
       *       them are really useful), we have to provide a dummy puts and putchar
       *         that are wrappers around printf.
       */
      int        puts(const char *);
      int        putchar(int c);
      
      int
      puts(const char *str)
      {
              printf("%s\n", str);
      
              return (0);
      }
      
      int
      putchar(int c)
      {
              printf("%c", c);
      
              return (c);
      }
      
      
      #endif
      /*        $OpenBSD: ip_input.c,v 1.344 2019/08/06 22:57:54 bluhm Exp $        */
      /*        $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $        */
      
      /*
       * 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.
       *
       *        @(#)ip_input.c        8.2 (Berkeley) 1/4/94
       */
      
      #include "pf.h"
      #include "carp.h"
      
      #include <sys/param.h>
      #include <sys/systm.h>
      #include <sys/mbuf.h>
      #include <sys/domain.h>
      #include <sys/mutex.h>
      #include <sys/protosw.h>
      #include <sys/socket.h>
      #include <sys/socketvar.h>
      #include <sys/sysctl.h>
      #include <sys/pool.h>
      #include <sys/task.h>
      
      #include <net/if.h>
      #include <net/if_var.h>
      #include <net/if_dl.h>
      #include <net/route.h>
      #include <net/netisr.h>
      
      #include <netinet/in.h>
      #include <netinet/in_systm.h>
      #include <netinet/if_ether.h>
      #include <netinet/ip.h>
      #include <netinet/in_pcb.h>
      #include <netinet/in_var.h>
      #include <netinet/ip_var.h>
      #include <netinet/ip_icmp.h>
      
      #ifdef INET6
      #include <netinet6/ip6protosw.h>
      #include <netinet6/ip6_var.h>
      #endif
      
      #if NPF > 0
      #include <net/pfvar.h>
      #endif
      
      #ifdef MROUTING
      #include <netinet/ip_mroute.h>
      #endif
      
      #ifdef IPSEC
      #include <netinet/ip_ipsp.h>
      #endif /* IPSEC */
      
      #if NCARP > 0
      #include <net/if_types.h>
      #include <netinet/ip_carp.h>
      #endif
      
      /* values controllable via sysctl */
      int        ipforwarding = 0;
      int        ipmforwarding = 0;
      int        ipmultipath = 0;
      int        ipsendredirects = 1;
      int        ip_dosourceroute = 0;
      int        ip_defttl = IPDEFTTL;
      int        ip_mtudisc = 1;
      u_int        ip_mtudisc_timeout = IPMTUDISCTIMEOUT;
      int        ip_directedbcast = 0;
      
      struct rttimer_queue *ip_mtudisc_timeout_q = NULL;
      
      /* Protects `ipq' and `ip_frags'. */
      struct mutex        ipq_mutex = MUTEX_INITIALIZER(IPL_SOFTNET);
      
      /* IP reassembly queue */
      LIST_HEAD(, ipq) ipq;
      
      /* Keep track of memory used for reassembly */
      int        ip_maxqueue = 300;
      int        ip_frags = 0;
      
      int *ipctl_vars[IPCTL_MAXID] = IPCTL_VARS;
      
      struct pool ipqent_pool;
      struct pool ipq_pool;
      
      struct cpumem *ipcounters;
      
      int ip_sysctl_ipstat(void *, size_t *, void *);
      
      static struct mbuf_queue        ipsend_mq;
      
      extern struct niqueue                arpinq;
      
      int        ip_ours(struct mbuf **, int *, int, int);
      int        ip_dooptions(struct mbuf *, struct ifnet *);
      int        in_ouraddr(struct mbuf *, struct ifnet *, struct rtentry **);
      
      static void ip_send_dispatch(void *);
      static struct task ipsend_task = TASK_INITIALIZER(ip_send_dispatch, &ipsend_mq);
      /*
       * Used to save the IP options in case a protocol wants to respond
       * to an incoming packet over the same route if the packet got here
       * using IP source routing.  This allows connection establishment and
       * maintenance when the remote end is on a network that is not known
       * to us.
       */
      struct ip_srcrt {
              int                isr_nhops;                   /* number of hops */
              struct in_addr        isr_dst;                   /* final destination */
              char                isr_nop;                   /* one NOP to align */
              char                isr_hdr[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN & OFFSET */
              struct in_addr        isr_routes[MAX_IPOPTLEN/sizeof(struct in_addr)];
      };
      
      void save_rte(struct mbuf *, u_char *, struct in_addr);
      
      /*
       * IP initialization: fill in IP protocol switch table.
       * All protocols not implemented in kernel go to raw IP protocol handler.
       */
      void
      ip_init(void)
      {
              const struct protosw *pr;
              int i;
              const u_int16_t defbaddynamicports_tcp[] = DEFBADDYNAMICPORTS_TCP;
              const u_int16_t defbaddynamicports_udp[] = DEFBADDYNAMICPORTS_UDP;
              const u_int16_t defrootonlyports_tcp[] = DEFROOTONLYPORTS_TCP;
              const u_int16_t defrootonlyports_udp[] = DEFROOTONLYPORTS_UDP;
      
              ipcounters = counters_alloc(ips_ncounters);
      
              pool_init(&ipqent_pool, sizeof(struct ipqent), 0,
                  IPL_SOFTNET, 0, "ipqe",  NULL);
              pool_init(&ipq_pool, sizeof(struct ipq), 0,
                  IPL_SOFTNET, 0, "ipq", NULL);
      
              pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
              if (pr == NULL)
                      panic("ip_init");
              for (i = 0; i < IPPROTO_MAX; i++)
                      ip_protox[i] = pr - inetsw;
              for (pr = inetdomain.dom_protosw;
                  pr < inetdomain.dom_protoswNPROTOSW; pr++)
                      if (pr->pr_domain->dom_family == PF_INET &&
                          pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW &&
                          pr->pr_protocol < IPPROTO_MAX)
                              ip_protox[pr->pr_protocol] = pr - inetsw;
              LIST_INIT(&ipq);
              if (ip_mtudisc != 0)
                      ip_mtudisc_timeout_q =
                          rt_timer_queue_create(ip_mtudisc_timeout);
      
              /* Fill in list of ports not to allocate dynamically. */
              memset(&baddynamicports, 0, sizeof(baddynamicports));
              for (i = 0; defbaddynamicports_tcp[i] != 0; i++)
                      DP_SET(baddynamicports.tcp, defbaddynamicports_tcp[i]);
              for (i = 0; defbaddynamicports_udp[i] != 0; i++)
                      DP_SET(baddynamicports.udp, defbaddynamicports_udp[i]);
      
              /* Fill in list of ports only root can bind to. */
              memset(&rootonlyports, 0, sizeof(rootonlyports));
              for (i = 0; defrootonlyports_tcp[i] != 0; i++)
                      DP_SET(rootonlyports.tcp, defrootonlyports_tcp[i]);
              for (i = 0; defrootonlyports_udp[i] != 0; i++)
                      DP_SET(rootonlyports.udp, defrootonlyports_udp[i]);
      
              mq_init(&ipsend_mq, 64, IPL_SOFTNET);
      
      #ifdef IPSEC
              ipsec_init();
      #endif
      }
      
      /*
       * IPv4 input routine.
       *
       * Checksum and byte swap header.  Process options. Forward or deliver.
       */
      void
      ipv4_input(struct ifnet *ifp, struct mbuf *m)
  105 {
              int off, nxt;
      
              off = 0;
              nxt = ip_input_if(&m, &off, IPPROTO_IPV4, AF_UNSPEC, ifp);
              KASSERT(nxt == IPPROTO_DONE);
  105 }
      
      int
      ip_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp)
  105 {
              struct mbuf        *m = *mp;
              struct rtentry        *rt = NULL;
              struct ip        *ip;
              int hlen, len;
              in_addr_t pfrdr = 0;
      
              KASSERT(*offp == 0);
      
              ipstat_inc(ips_total);
  104         if (m->m_len < sizeof (struct ip) &&
                  (m = *mp = m_pullup(m, sizeof (struct ip))) == NULL) {
    1                 ipstat_inc(ips_toosmall);
                      goto bad;
              }
              ip = mtod(m, struct ip *);
              if (ip->ip_v != IPVERSION) {
    1                 ipstat_inc(ips_badvers);
                      goto bad;
              }
              hlen = ip->ip_hl << 2;
              if (hlen < sizeof(struct ip)) {        /* minimum header length */
                      ipstat_inc(ips_badhlen);
                      goto bad;
              }
  102         if (hlen > m->m_len) {
                      if ((m = *mp = m_pullup(m, hlen)) == NULL) {
    1                         ipstat_inc(ips_badhlen);
                              goto bad;
                      }
                      ip = mtod(m, struct ip *);
              }
      
              /* 127/8 must not appear on wire - RFC1122 */
  102         if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
                  (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
   17                 if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
    2                         ipstat_inc(ips_badaddr);
                              goto bad;
                      }
              }
      
              if ((m->m_pkthdr.csum_flags & M_IPV4_CSUM_IN_OK) == 0) {
                      if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_IN_BAD) {
                              ipstat_inc(ips_badsum);
                              goto bad;
                      }
      
                      ipstat_inc(ips_inswcsum);
   99                 if (in_cksum(m, hlen) != 0) {
    1                         ipstat_inc(ips_badsum);
                              goto bad;
                      }
              }
      
              /* Retrieve the packet length. */
              len = ntohs(ip->ip_len);
      
              /*
               * Convert fields to host representation.
               */
              if (len < hlen) {
                      ipstat_inc(ips_badlen);
                      goto bad;
              }
      
              /*
               * Check that the amount of data in the buffers
               * is at least as much as the IP header would have us expect.
               * Trim mbufs if longer than we expect.
               * Drop packet if shorter than we expect.
               */
              if (m->m_pkthdr.len < len) {
                      ipstat_inc(ips_tooshort);
                      goto bad;
              }
   99         if (m->m_pkthdr.len > len) {
                      if (m->m_len == m->m_pkthdr.len) {
                              m->m_len = len;
                              m->m_pkthdr.len = len;
                      } else
                              m_adj(m, len - m->m_pkthdr.len);
              }
      
      #if NCARP > 0
              if (carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
                  &ip->ip_dst.s_addr, (ip->ip_p == IPPROTO_ICMP ? 0 : 1)))
                      goto bad;
      #endif
      
      #if NPF > 0
              /*
               * Packet filter
               */
              pfrdr = ip->ip_dst.s_addr;
    9         if (pf_test(AF_INET, PF_IN, ifp, mp) != PF_PASS)
                      goto bad;
              m = *mp;
   14         if (m == NULL)
                      goto bad;
      
              ip = mtod(m, struct ip *);
              hlen = ip->ip_hl << 2;
              pfrdr = (pfrdr != ip->ip_dst.s_addr);
      #endif
      
              /*
               * Process options and, if not destined for us,
               * ship it on.  ip_dooptions returns 1 when an
               * error was detected (causing an icmp message
               * to be sent and the original packet to be freed).
               */
   85         if (hlen > sizeof (struct ip) && ip_dooptions(m, ifp)) {
    1                 m = *mp = NULL;
                      goto bad;
              }
      
   82         if (ip->ip_dst.s_addr == INADDR_BROADCAST ||
                  ip->ip_dst.s_addr == INADDR_ANY) {
                      nxt = ip_ours(mp, offp, nxt, af);
                      goto out;
              }
      
              if (in_ouraddr(m, ifp, &rt)) {
    1                 nxt = ip_ours(mp, offp, nxt, af);
                      goto out;
              }
      
              if (IN_MULTICAST(ip->ip_dst.s_addr)) {
                      /*
                       * Make sure M_MCAST is set.  It should theoretically
                       * already be there, but let's play safe because upper
                       * layers check for this flag.
                       */
                      m->m_flags |= M_MCAST;
      
      #ifdef MROUTING
   16                 if (ipmforwarding && ip_mrouter[ifp->if_rdomain]) {
                              int error;
      
                              if (m->m_flags & M_EXT) {
                                      if ((m = *mp = m_pullup(m, hlen)) == NULL) {
                                              ipstat_inc(ips_toosmall);
                                              goto bad;
                                      }
                                      ip = mtod(m, struct ip *);
                              }
                              /*
                               * If we are acting as a multicast router, all
                               * incoming multicast packets are passed to the
                               * kernel-level multicast forwarding function.
                               * The packet is returned (relatively) intact; if
                               * ip_mforward() returns a non-zero value, the packet
                               * must be discarded, else it may be accepted below.
                               *
                               * (The IP ident field is put in the same byte order
                               * as expected when ip_mforward() is called from
                               * ip_output().)
                               */
                              KERNEL_LOCK();
                              error = ip_mforward(m, ifp);
                              KERNEL_UNLOCK();
                              if (error) {
                                      ipstat_inc(ips_cantforward);
                                      goto bad;
                              }
      
                              /*
                               * The process-level routing daemon needs to receive
                               * all multicast IGMP packets, whether or not this
                               * host belongs to their destination groups.
                               */
                              if (ip->ip_p == IPPROTO_IGMP) {
                                      nxt = ip_ours(mp, offp, nxt, af);
                                      goto out;
                              }
                              ipstat_inc(ips_forward);
                      }
      #endif
                      /*
                       * See if we belong to the destination multicast group on the
                       * arrival interface.
                       */
                      if (!in_hasmulti(&ip->ip_dst, ifp)) {
                              ipstat_inc(ips_notmember);
                              if (!IN_LOCAL_GROUP(ip->ip_dst.s_addr))
                                      ipstat_inc(ips_cantforward);
                              goto bad;
                      }
   16                 nxt = ip_ours(mp, offp, nxt, af);
                      goto out;
              }
      
      #if NCARP > 0
              if (ip->ip_p == IPPROTO_ICMP &&
                  carp_lsdrop(ifp, m, AF_INET, &ip->ip_src.s_addr,
                  &ip->ip_dst.s_addr, 1))
                      goto bad;
      #endif
              /*
               * Not for us; forward if possible and desirable.
               */
              if (ipforwarding == 0) {
                      ipstat_inc(ips_cantforward);
                      goto bad;
              }
      #ifdef IPSEC
              if (ipsec_in_use) {
                      int rv;
      
                      rv = ipsec_forward_check(m, hlen, AF_INET);
                      if (rv != 0) {
                              ipstat_inc(ips_cantforward);
                              goto bad;
                      }
                      /*
                       * Fall through, forward packet. Outbound IPsec policy
                       * checking will occur in ip_output().
                       */
              }
      #endif /* IPSEC */
      
              ip_forward(m, ifp, rt, pfrdr);
              *mp = NULL;
              return IPPROTO_DONE;
       bad:
              nxt = IPPROTO_DONE;
              m_freemp(mp);
       out:
              rtfree(rt);
              return nxt;
      }
      
      /*
       * IPv4 local-delivery routine.
       *
       * If fragmented try to reassemble.  Pass to next level.
       */
      int
      ip_ours(struct mbuf **mp, int *offp, int nxt, int af)
   92 {
              struct mbuf *m = *mp;
              struct ip *ip = mtod(m, struct ip *);
              struct ipq *fp;
              struct ipqent *ipqe;
              int mff, hlen;
      
              hlen = ip->ip_hl << 2;
      
              /*
               * If offset or IP_MF are set, must reassemble.
               * Otherwise, nothing need be done.
               * (We could look in the reassembly queue to see
               * if the packet was previously fragmented,
               * but it's not worth the time; just let them time out.)
               */
   92         if (ip->ip_off &~ htons(IP_DF | IP_RF)) {
                      if (m->m_flags & M_EXT) {                /* XXX */
                              if ((m = *mp = m_pullup(m, hlen)) == NULL) {
                                      ipstat_inc(ips_toosmall);
                                      return IPPROTO_DONE;
                              }
                              ip = mtod(m, struct ip *);
                      }
      
                      mtx_enter(&ipq_mutex);
      
                      /*
                       * Look for queue of fragments
                       * of this datagram.
                       */
                      LIST_FOREACH(fp, &ipq, ipq_q) {
                              if (ip->ip_id == fp->ipq_id &&
                                  ip->ip_src.s_addr == fp->ipq_src.s_addr &&
                                  ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
                                  ip->ip_p == fp->ipq_p)
                                      break;
                      }
      
                      /*
                       * Adjust ip_len to not reflect header,
                       * set ipqe_mff if more fragments are expected,
                       * convert offset of this to bytes.
                       */
                      ip->ip_len = htons(ntohs(ip->ip_len) - hlen);
                      mff = (ip->ip_off & htons(IP_MF)) != 0;
                      if (mff) {
                              /*
                               * Make sure that fragments have a data length
                               * that's a non-zero multiple of 8 bytes.
                               */
                              if (ntohs(ip->ip_len) == 0 ||
                                  (ntohs(ip->ip_len) & 0x7) != 0) {
                                      ipstat_inc(ips_badfrags);
                                      goto bad;
                              }
                      }
                      ip->ip_off = htons(ntohs(ip->ip_off) << 3);
      
                      /*
                       * If datagram marked as having more fragments
                       * or if this is not the first fragment,
                       * attempt reassembly; if it succeeds, proceed.
                       */
                      if (mff || ip->ip_off) {
                              ipstat_inc(ips_fragments);
                              if (ip_frags + 1 > ip_maxqueue) {
                                      ip_flush();
                                      ipstat_inc(ips_rcvmemdrop);
                                      goto bad;
                              }
      
                              ipqe = pool_get(&ipqent_pool, PR_NOWAIT);
                              if (ipqe == NULL) {
                                      ipstat_inc(ips_rcvmemdrop);
                                      goto bad;
                              }
                              ip_frags++;
                              ipqe->ipqe_mff = mff;
                              ipqe->ipqe_m = m;
                              ipqe->ipqe_ip = ip;
                              m = *mp = ip_reass(ipqe, fp);
                              if (m == NULL)
                                      goto bad;
                              ipstat_inc(ips_reassembled);
                              ip = mtod(m, struct ip *);
                              hlen = ip->ip_hl << 2;
                              ip->ip_len = htons(ntohs(ip->ip_len) + hlen);
                      } else
                              if (fp)
                                      ip_freef(fp);
      
                      mtx_leave(&ipq_mutex);
              }
      
              *offp = hlen;
              nxt = ip->ip_p;
              /* Check wheter we are already in a IPv4/IPv6 local deliver loop. */
              if (af == AF_UNSPEC)
   92                 nxt = ip_deliver(mp, offp, nxt, AF_INET);
              return nxt;
       bad:
              mtx_leave(&ipq_mutex);
              m_freemp(mp);
              return IPPROTO_DONE;
      }
      
      #ifndef INET6
      #define IPSTAT_INC(name)        ipstat_inc(ips_##name)
      #else
      #define IPSTAT_INC(name)        (af == AF_INET ?        \
          ipstat_inc(ips_##name) : ip6stat_inc(ip6s_##name))
      #endif
      
      int
      ip_deliver(struct mbuf **mp, int *offp, int nxt, int af)
  101 {
    9         const struct protosw *psw;
    2         int naf = af;
      #ifdef INET6
              int nest = 0;
      #endif /* INET6 */
      
              /* pf might have modified stuff, might have to chksum */
              switch (af) {
              case AF_INET:
   92                 in_proto_cksum_out(*mp, NULL);
                      break;
      #ifdef INET6
              case AF_INET6:
    9                 in6_proto_cksum_out(*mp, NULL);
                      break;
      #endif /* INET6 */
              }
      
              /*
               * Tell launch routine the next header
               */
              IPSTAT_INC(delivered);
      
  101         while (nxt != IPPROTO_DONE) {
      #ifdef INET6
  101                 if (af == AF_INET6 &&
                          ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
                              ip6stat_inc(ip6s_toomanyhdr);
                              goto bad;
                      }
      #endif /* INET6 */
      
                      /*
                       * protection against faulty packet - there should be
                       * more sanity checks in header chain processing.
                       */
                      if ((*mp)->m_pkthdr.len < *offp) {
                              IPSTAT_INC(tooshort);
                              goto bad;
                      }
      
      #ifdef INET6
                      /* draft-itojun-ipv6-tcp-to-anycast */
   92                 if (af == AF_INET6 &&
    9                     ISSET((*mp)->m_flags, M_ACAST) && (nxt == IPPROTO_TCP)) {
                              if ((*mp)->m_len >= sizeof(struct ip6_hdr)) {
                                      icmp6_error(*mp, ICMP6_DST_UNREACH,
                                              ICMP6_DST_UNREACH_ADDR,
                                              offsetof(struct ip6_hdr, ip6_dst));
                                      *mp = NULL;
                              }
                              goto bad;
                      }
      #endif /* INET6 */
      
      #ifdef IPSEC
  101                 if (ipsec_in_use) {
                              if (ipsec_local_check(*mp, *offp, nxt, af) != 0) {
                                      IPSTAT_INC(cantforward);
                                      goto bad;
                              }
                      }
                      /* Otherwise, just fall through and deliver the packet */
      #endif /* IPSEC */
      
   99                 switch (nxt) {
                      case IPPROTO_IPV4:
                              naf = AF_INET;
                              ipstat_inc(ips_delivered);
                              break;
      #ifdef INET6
                      case IPPROTO_IPV6:
                              naf = AF_INET6;
                              ip6stat_inc(ip6s_delivered);
                              break;
      #endif /* INET6 */
                      }
   92                 switch (af) {
                      case AF_INET:
                              psw = &inetsw[ip_protox[nxt]];
                              break;
      #ifdef INET6
                      case AF_INET6:
                              psw = &inet6sw[ip6_protox[nxt]];
                              break;
      #endif /* INET6 */
                      }
                      nxt = (*psw->pr_input)(mp, offp, nxt, af);
                      af = naf;
              }
              return nxt;
       bad:
              m_freemp(mp);
              return IPPROTO_DONE;
      }
      #undef IPSTAT_INC
      
      int
      in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct rtentry **prt)
   17 {
              struct rtentry                *rt;
              struct ip                *ip;
              struct sockaddr_in         sin;
              int                         match = 0;
      
      #if NPF > 0
              switch (pf_ouraddr(m)) {
              case 0:
                      return (0);
              case 1:
                      return (1);
              default:
                      /* pf does not know it */
                      break;
              }
      #endif
      
              ip = mtod(m, struct ip *);
      
              memset(&sin, 0, sizeof(sin));
              sin.sin_len = sizeof(sin);
              sin.sin_family = AF_INET;
              sin.sin_addr = ip->ip_dst;
              rt = rtalloc_mpath(sintosa(&sin), &ip->ip_src.s_addr,
                  m->m_pkthdr.ph_rtableid);
              if (rtisvalid(rt)) {
                      if (ISSET(rt->rt_flags, RTF_LOCAL))
                              match = 1;
      
                      /*
                       * If directedbcast is enabled we only consider it local
                       * if it is received on the interface with that address.
                       */
   16                 if (ISSET(rt->rt_flags, RTF_BROADCAST) &&
    1                     (!ip_directedbcast || rt->rt_ifidx == ifp->if_index)) {
                              match = 1;
      
                              /* Make sure M_BCAST is set */
                              m->m_flags |= M_BCAST;
                      }
              }
              *prt = rt;
      
   16         if (!match) {
                      struct ifaddr *ifa;
      
                      /*
                       * No local address or broadcast address found, so check for
                       * ancient classful broadcast addresses.
                       * It must have been broadcast on the link layer, and for an
                       * address on the interface it was received on.
                       */
   16                 if (!ISSET(m->m_flags, M_BCAST) ||
                          !IN_CLASSFULBROADCAST(ip->ip_dst.s_addr, ip->ip_dst.s_addr))
                              return (0);
      
                      if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid))
                              return (0);
                      /*
                       * The check in the loop assumes you only rx a packet on an UP
                       * interface, and that M_BCAST will only be set on a BROADCAST
                       * interface.
                       */
                      NET_ASSERT_LOCKED();
                      TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
                              if (ifa->ifa_addr->sa_family != AF_INET)
                                      continue;
      
                              if (IN_CLASSFULBROADCAST(ip->ip_dst.s_addr,
                                  ifatoia(ifa)->ia_addr.sin_addr.s_addr)) {
                                      match = 1;
                                      break;
                              }
                      }
              }
      
              return (match);
      }
      
      /*
       * Take incoming datagram fragment and try to
       * reassemble it into whole datagram.  If a chain for
       * reassembly of this datagram already exists, then it
       * is given as fp; otherwise have to make a chain.
       */
      struct mbuf *
      ip_reass(struct ipqent *ipqe, struct ipq *fp)
      {
              struct mbuf *m = ipqe->ipqe_m;
              struct ipqent *nq, *p, *q;
              struct ip *ip;
              struct mbuf *t;
              int hlen = ipqe->ipqe_ip->ip_hl << 2;
              int i, next;
              u_int8_t ecn, ecn0;
      
              MUTEX_ASSERT_LOCKED(&ipq_mutex);
      
              /*
               * Presence of header sizes in mbufs
               * would confuse code below.
               */
              m->m_data += hlen;
              m->m_len -= hlen;
      
              /*
               * If first fragment to arrive, create a reassembly queue.
               */
              if (fp == NULL) {
                      fp = pool_get(&ipq_pool, PR_NOWAIT);
                      if (fp == NULL)
                              goto dropfrag;
                      LIST_INSERT_HEAD(&ipq, fp, ipq_q);
                      fp->ipq_ttl = IPFRAGTTL;
                      fp->ipq_p = ipqe->ipqe_ip->ip_p;
                      fp->ipq_id = ipqe->ipqe_ip->ip_id;
                      LIST_INIT(&fp->ipq_fragq);
                      fp->ipq_src = ipqe->ipqe_ip->ip_src;
                      fp->ipq_dst = ipqe->ipqe_ip->ip_dst;
                      p = NULL;
                      goto insert;
              }
      
              /*
               * Handle ECN by comparing this segment with the first one;
               * if CE is set, do not lose CE.
               * drop if CE and not-ECT are mixed for the same packet.
               */
              ecn = ipqe->ipqe_ip->ip_tos & IPTOS_ECN_MASK;
              ecn0 = LIST_FIRST(&fp->ipq_fragq)->ipqe_ip->ip_tos & IPTOS_ECN_MASK;
              if (ecn == IPTOS_ECN_CE) {
                      if (ecn0 == IPTOS_ECN_NOTECT)
                              goto dropfrag;
                      if (ecn0 != IPTOS_ECN_CE)
                              LIST_FIRST(&fp->ipq_fragq)->ipqe_ip->ip_tos |=
                                  IPTOS_ECN_CE;
              }
              if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT)
                      goto dropfrag;
      
              /*
               * Find a segment which begins after this one does.
               */
              for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
                  p = q, q = LIST_NEXT(q, ipqe_q))
                      if (ntohs(q->ipqe_ip->ip_off) > ntohs(ipqe->ipqe_ip->ip_off))
                              break;
      
              /*
               * If there is a preceding segment, it may provide some of
               * our data already.  If so, drop the data from the incoming
               * segment.  If it provides all of our data, drop us.
               */
              if (p != NULL) {
                      i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) -
                          ntohs(ipqe->ipqe_ip->ip_off);
                      if (i > 0) {
                              if (i >= ntohs(ipqe->ipqe_ip->ip_len))
                                      goto dropfrag;
                              m_adj(ipqe->ipqe_m, i);
                              ipqe->ipqe_ip->ip_off =
                                  htons(ntohs(ipqe->ipqe_ip->ip_off) + i);
                              ipqe->ipqe_ip->ip_len =
                                  htons(ntohs(ipqe->ipqe_ip->ip_len) - i);
                      }
              }
      
              /*
               * While we overlap succeeding segments trim them or,
               * if they are completely covered, dequeue them.
               */
              for (; q != NULL &&
                  ntohs(ipqe->ipqe_ip->ip_off) + ntohs(ipqe->ipqe_ip->ip_len) >
                  ntohs(q->ipqe_ip->ip_off); q = nq) {
                      i = (ntohs(ipqe->ipqe_ip->ip_off) +
                          ntohs(ipqe->ipqe_ip->ip_len)) - ntohs(q->ipqe_ip->ip_off);
                      if (i < ntohs(q->ipqe_ip->ip_len)) {
                              q->ipqe_ip->ip_len =
                                  htons(ntohs(q->ipqe_ip->ip_len) - i);
                              q->ipqe_ip->ip_off =
                                  htons(ntohs(q->ipqe_ip->ip_off) + i);
                              m_adj(q->ipqe_m, i);
                              break;
                      }
                      nq = LIST_NEXT(q, ipqe_q);
                      m_freem(q->ipqe_m);
                      LIST_REMOVE(q, ipqe_q);
                      pool_put(&ipqent_pool, q);
                      ip_frags--;
              }
      
      insert:
              /*
               * Stick new segment in its place;
               * check for complete reassembly.
               */
              if (p == NULL) {
                      LIST_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q);
              } else {
                      LIST_INSERT_AFTER(p, ipqe, ipqe_q);
              }
              next = 0;
              for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
                  p = q, q = LIST_NEXT(q, ipqe_q)) {
                      if (ntohs(q->ipqe_ip->ip_off) != next)
                              return (0);
                      next += ntohs(q->ipqe_ip->ip_len);
              }
              if (p->ipqe_mff)
                      return (0);
      
              /*
               * Reassembly is complete.  Check for a bogus message size and
               * concatenate fragments.
               */
              q = LIST_FIRST(&fp->ipq_fragq);
              ip = q->ipqe_ip;
              if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) {
                      ipstat_inc(ips_toolong);
                      ip_freef(fp);
                      return (0);
              }
              m = q->ipqe_m;
              t = m->m_next;
              m->m_next = 0;
              m_cat(m, t);
              nq = LIST_NEXT(q, ipqe_q);
              pool_put(&ipqent_pool, q);
              ip_frags--;
              for (q = nq; q != NULL; q = nq) {
                      t = q->ipqe_m;
                      nq = LIST_NEXT(q, ipqe_q);
                      pool_put(&ipqent_pool, q);
                      ip_frags--;
                      m_removehdr(t);
                      m_cat(m, t);
              }
      
              /*
               * Create header for new ip packet by
               * modifying header of first packet;
               * dequeue and discard fragment reassembly header.
               * Make header visible.
               */
              ip->ip_len = htons(next);
              ip->ip_src = fp->ipq_src;
              ip->ip_dst = fp->ipq_dst;
              LIST_REMOVE(fp, ipq_q);
              pool_put(&ipq_pool, fp);
              m->m_len += (ip->ip_hl << 2);
              m->m_data -= (ip->ip_hl << 2);
              m_calchdrlen(m);
              return (m);
      
      dropfrag:
              ipstat_inc(ips_fragdropped);
              m_freem(m);
              pool_put(&ipqent_pool, ipqe);
              ip_frags--;
              return (NULL);
      }
      
      /*
       * Free a fragment reassembly header and all
       * associated datagrams.
       */
      void
      ip_freef(struct ipq *fp)
      {
              struct ipqent *q;
      
              MUTEX_ASSERT_LOCKED(&ipq_mutex);
      
              while ((q = LIST_FIRST(&fp->ipq_fragq)) != NULL) {
                      LIST_REMOVE(q, ipqe_q);
                      m_freem(q->ipqe_m);
                      pool_put(&ipqent_pool, q);
                      ip_frags--;
              }
              LIST_REMOVE(fp, ipq_q);
              pool_put(&ipq_pool, fp);
      }
      
      /*
       * IP timer processing;
       * if a timer expires on a reassembly queue, discard it.
       */
      void
      ip_slowtimo(void)
      {
              struct ipq *fp, *nfp;
      
              mtx_enter(&ipq_mutex);
              LIST_FOREACH_SAFE(fp, &ipq, ipq_q, nfp) {
                      if (--fp->ipq_ttl == 0) {
                              ipstat_inc(ips_fragtimeout);
                              ip_freef(fp);
                      }
              }
              mtx_leave(&ipq_mutex);
      }
      
      /*
       * Flush a bunch of datagram fragments, till we are down to 75%.
       */
      void
      ip_flush(void)
      {
              int max = 50;
      
              MUTEX_ASSERT_LOCKED(&ipq_mutex);