BSD 4_4 release
[unix-history] / usr / src / sys / netinet / raw_ip.c
index 3825222..cf1a8d0 100644 (file)
 /*
 /*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution is only permitted until one year after the first shipment
- * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
- * binary forms are permitted provided that: (1) source distributions retain
- * this entire copyright notice and comment, and (2) distributions including
- * binaries display the following acknowledgement:  This product includes
- * software developed by the University of California, Berkeley and its
- * contributors'' in the documentation or other materials provided with the
- * distribution and in all advertising materials mentioning features or use
- * of this software.  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 AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. 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.
  *
  *
- *     @(#)raw_ip.c    7.8 (Berkeley) 7/25/90
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)raw_ip.c    8.1 (Berkeley) 6/10/93
  */
 
  */
 
-#include "param.h"
-#include "malloc.h"
-#include "mbuf.h"
-#include "socket.h"
-#include "protosw.h"
-#include "socketvar.h"
-#include "errno.h"
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in_pcb.h>
 
 
-#include "../net/if.h"
-#include "../net/route.h"
-#include "../net/raw_cb.h"
+struct inpcb rawinpcb;
 
 
-#include "in.h"
-#include "in_systm.h"
-#include "ip.h"
-#include "ip_var.h"
-#include "in_pcb.h"
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+#define        RIPSNDQ         8192
+#define        RIPRCVQ         8192
 
 /*
  * Raw interface to IP protocol.
  */
 
 
 /*
  * Raw interface to IP protocol.
  */
 
-struct sockaddr_in ripdst = { sizeof(ripdst), AF_INET };
+/*
+ * Initialize raw connection block q.
+ */
+void
+rip_init()
+{
+
+       rawinpcb.inp_next = rawinpcb.inp_prev = &rawinpcb;
+}
+
 struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET };
 struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET };
-struct sockproto ripproto = { PF_INET };
 /*
  * Setup generic address and protocol structures
  * for raw_input routine, then pass them along with
  * mbuf chain.
  */
 /*
  * Setup generic address and protocol structures
  * for raw_input routine, then pass them along with
  * mbuf chain.
  */
+void
 rip_input(m)
        struct mbuf *m;
 {
        register struct ip *ip = mtod(m, struct ip *);
 rip_input(m)
        struct mbuf *m;
 {
        register struct ip *ip = mtod(m, struct ip *);
+       register struct inpcb *inp;
+       struct socket *last = 0;
 
 
-       ripproto.sp_protocol = ip->ip_p;
-       ripdst.sin_addr = ip->ip_dst;
        ripsrc.sin_addr = ip->ip_src;
        ripsrc.sin_addr = ip->ip_src;
-       if (raw_input(m, &ripproto, (struct sockaddr *)&ripsrc,
-         (struct sockaddr *)&ripdst) == 0) {
+       for (inp = rawinpcb.inp_next; inp != &rawinpcb; inp = inp->inp_next) {
+               if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
+                       continue;
+               if (inp->inp_laddr.s_addr &&
+                   inp->inp_laddr.s_addr == ip->ip_dst.s_addr)
+                       continue;
+               if (inp->inp_faddr.s_addr &&
+                   inp->inp_faddr.s_addr == ip->ip_src.s_addr)
+                       continue;
+               if (last) {
+                       struct mbuf *n;
+                       if (n = m_copy(m, 0, (int)M_COPYALL)) {
+                               if (sbappendaddr(&last->so_rcv, &ripsrc,
+                                   n, (struct mbuf *)0) == 0)
+                                       /* should notify about lost packet */
+                                       m_freem(n);
+                               else
+                                       sorwakeup(last);
+                       }
+               }
+               last = inp->inp_socket;
+       }
+       if (last) {
+               if (sbappendaddr(&last->so_rcv, &ripsrc,
+                   m, (struct mbuf *)0) == 0)
+                       m_freem(m);
+               else
+                       sorwakeup(last);
+       } else {
+               m_freem(m);
                ipstat.ips_noproto++;
                ipstat.ips_delivered--;
        }
                ipstat.ips_noproto++;
                ipstat.ips_delivered--;
        }
@@ -69,180 +128,263 @@ rip_input(m)
  * Generate IP header and pass packet to ip_output.
  * Tack on options user may have setup with control call.
  */
  * Generate IP header and pass packet to ip_output.
  * Tack on options user may have setup with control call.
  */
-#define        satosin(sa)     ((struct sockaddr_in *)(sa))
-rip_output(m, so)
+int
+rip_output(m, so, dst)
        register struct mbuf *m;
        struct socket *so;
        register struct mbuf *m;
        struct socket *so;
+       u_long dst;
 {
        register struct ip *ip;
 {
        register struct ip *ip;
-       register struct raw_inpcb *rp = sotorawinpcb(so);
+       register struct inpcb *inp = sotoinpcb(so);
        register struct sockaddr_in *sin;
        register struct sockaddr_in *sin;
+       struct mbuf *opts;
+       int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
 
        /*
         * If the user handed us a complete IP packet, use it.
         * Otherwise, allocate an mbuf for a header and fill it in.
         */
 
        /*
         * If the user handed us a complete IP packet, use it.
         * Otherwise, allocate an mbuf for a header and fill it in.
         */
-       if (rp->rinp_flags & RINPF_HDRINCL)
-               ip = mtod(m, struct ip *);
-       else {
+       if ((inp->inp_flags & INP_HDRINCL) == 0) {
                M_PREPEND(m, sizeof(struct ip), M_WAIT);
                ip = mtod(m, struct ip *);
                ip->ip_tos = 0;
                ip->ip_off = 0;
                M_PREPEND(m, sizeof(struct ip), M_WAIT);
                ip = mtod(m, struct ip *);
                ip->ip_tos = 0;
                ip->ip_off = 0;
-               ip->ip_p = rp->rinp_rcb.rcb_proto.sp_protocol;
+               ip->ip_p = inp->inp_ip.ip_p;
                ip->ip_len = m->m_pkthdr.len;
                ip->ip_len = m->m_pkthdr.len;
-               if (sin = satosin(rp->rinp_rcb.rcb_laddr)) {
-                       ip->ip_src = sin->sin_addr;
-               } else
-                       ip->ip_src.s_addr = 0;
-               if (sin = satosin(rp->rinp_rcb.rcb_faddr))
-                   ip->ip_dst = sin->sin_addr;
+               ip->ip_src = inp->inp_laddr;
+               ip->ip_dst.s_addr = dst;
                ip->ip_ttl = MAXTTL;
                ip->ip_ttl = MAXTTL;
+               opts = inp->inp_options;
+       } else {
+               ip = mtod(m, struct ip *);
+               if (ip->ip_id == 0)
+                       ip->ip_id = htons(ip_id++);
+               opts = NULL;
+               /* XXX prevent ip_output from overwriting header fields */
+               flags |= IP_RAWOUTPUT;
+               ipstat.ips_rawout++;
        }
        }
-       return (ip_output(m,
-          (rp->rinp_flags & RINPF_HDRINCL)? (struct mbuf *)0: rp->rinp_options,
-           &rp->rinp_route, 
-          (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST));
+       return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions));
 }
 
 /*
  * Raw IP socket option processing.
  */
 }
 
 /*
  * Raw IP socket option processing.
  */
+int
 rip_ctloutput(op, so, level, optname, m)
        int op;
        struct socket *so;
        int level, optname;
        struct mbuf **m;
 {
 rip_ctloutput(op, so, level, optname, m)
        int op;
        struct socket *so;
        int level, optname;
        struct mbuf **m;
 {
-       int error = 0;
-       register struct raw_inpcb *rp = sotorawinpcb(so);
+       register struct inpcb *inp = sotoinpcb(so);
+       register int error;
 
        if (level != IPPROTO_IP)
 
        if (level != IPPROTO_IP)
-               error = EINVAL;
-       else switch (op) {
+               return (EINVAL);
 
 
-       case PRCO_SETOPT:
-               switch (optname) {
+       switch (optname) {
 
 
-               case IP_OPTIONS:
-                       return (ip_pcbopts(&rp->rinp_options, *m));
-
-               case IP_HDRINCL:
-                       if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int)) {
-                               error = EINVAL;
-                               break;
+       case IP_HDRINCL:
+               if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
+                       if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int))
+                               return (EINVAL);
+                       if (op == PRCO_SETOPT) {
+                               if (*mtod(*m, int *))
+                                       inp->inp_flags |= INP_HDRINCL;
+                               else
+                                       inp->inp_flags &= ~INP_HDRINCL;
+                               (void)m_free(*m);
+                       } else {
+                               (*m)->m_len = sizeof (int);
+                               *mtod(*m, int *) = inp->inp_flags & INP_HDRINCL;
                        }
                        }
-                       if (*mtod(*m, int *))
-                               rp->rinp_flags |= RINPF_HDRINCL;
-                       else
-                               rp->rinp_flags &= ~RINPF_HDRINCL;
-                       break;
-
-               default:
-                       error = EINVAL;
-                       break;
+                       return (0);
                }
                break;
 
                }
                break;
 
-       case PRCO_GETOPT:
-               *m = m_get(M_WAIT, MT_SOOPTS);
-               switch (optname) {
-
-               case IP_OPTIONS:
-                       if (rp->rinp_options) {
-                               (*m)->m_len = rp->rinp_options->m_len;
-                               bcopy(mtod(rp->rinp_options, caddr_t),
-                                   mtod(*m, caddr_t), (unsigned)(*m)->m_len);
-                       } else
-                               (*m)->m_len = 0;
-                       break;
-
-               case IP_HDRINCL:
-                       (*m)->m_len = sizeof (int);
-                       *mtod(*m, int *) = rp->rinp_flags & RINPF_HDRINCL;
-                       break;
-
-               default:
+       case DVMRP_INIT:
+       case DVMRP_DONE:
+       case DVMRP_ADD_VIF:
+       case DVMRP_DEL_VIF:
+       case DVMRP_ADD_LGRP:
+       case DVMRP_DEL_LGRP:
+       case DVMRP_ADD_MRT:
+       case DVMRP_DEL_MRT:
+#ifdef MROUTING
+               if (op == PRCO_SETOPT) {
+                       error = ip_mrouter_cmd(optname, so, *m);
+                       if (*m)
+                               (void)m_free(*m);
+               } else
                        error = EINVAL;
                        error = EINVAL;
-                       m_freem(*m);
-                       *m = 0;
-                       break;
-               }
-               break;
+               return (error);
+#else
+               if (op == PRCO_SETOPT && *m)
+                       (void)m_free(*m);
+               return (EOPNOTSUPP);
+#endif
        }
        }
-       if (op == PRCO_SETOPT && *m)
-               (void)m_free(*m);
-       return (error);
+       return (ip_ctloutput(op, so, level, optname, m));
 }
 
 }
 
+u_long rip_sendspace = RIPSNDQ;
+u_long rip_recvspace = RIPRCVQ;
+
 /*ARGSUSED*/
 /*ARGSUSED*/
-rip_usrreq(so, req, m, nam, rights, control)
+int
+rip_usrreq(so, req, m, nam, control)
        register struct socket *so;
        int req;
        register struct socket *so;
        int req;
-       struct mbuf *m, *nam, *rights, *control;
+       struct mbuf *m, *nam, *control;
 {
        register int error = 0;
 {
        register int error = 0;
-       register struct raw_inpcb *rp = sotorawinpcb(so);
-
+       register struct inpcb *inp = sotoinpcb(so);
+#ifdef MROUTING
+       extern struct socket *ip_mrouter;
+#endif
        switch (req) {
 
        case PRU_ATTACH:
        switch (req) {
 
        case PRU_ATTACH:
-               if (rp)
+               if (inp)
                        panic("rip_attach");
                        panic("rip_attach");
-               MALLOC(rp, struct raw_inpcb *, sizeof *rp, M_PCB, M_WAITOK);
-               if (rp == 0)
-                       return (ENOBUFS);
-               bzero((caddr_t)rp, sizeof *rp);
-               so->so_pcb = (caddr_t)rp;
+               if ((so->so_state & SS_PRIV) == 0) {
+                       error = EACCES;
+                       break;
+               }
+               if ((error = soreserve(so, rip_sendspace, rip_recvspace)) ||
+                   (error = in_pcballoc(so, &rawinpcb)))
+                       break;
+               inp = (struct inpcb *)so->so_pcb;
+               inp->inp_ip.ip_p = (int)nam;
                break;
 
                break;
 
+       case PRU_DISCONNECT:
+               if ((so->so_state & SS_ISCONNECTED) == 0) {
+                       error = ENOTCONN;
+                       break;
+               }
+               /* FALLTHROUGH */
+       case PRU_ABORT:
+               soisdisconnected(so);
+               /* FALLTHROUGH */
        case PRU_DETACH:
        case PRU_DETACH:
-               if (rp == 0)
+               if (inp == 0)
                        panic("rip_detach");
                        panic("rip_detach");
-               if (rp->rinp_options)
-                       m_freem(rp->rinp_options);
-               if (rp->rinp_route.ro_rt)
-                       RTFREE(rp->rinp_route.ro_rt);
-               if (rp->rinp_rcb.rcb_laddr)
-                       rp->rinp_rcb.rcb_laddr = 0;
+#ifdef MROUTING
+               if (so == ip_mrouter)
+                       ip_mrouter_done();
+#endif
+               in_pcbdetach(inp);
                break;
 
        case PRU_BIND:
            {
                struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
 
                break;
 
        case PRU_BIND:
            {
                struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
 
-               if (nam->m_len != sizeof(*addr))
-                       return (EINVAL);
+               if (nam->m_len != sizeof(*addr)) {
+                       error = EINVAL;
+                       break;
+               }
                if ((ifnet == 0) ||
                    ((addr->sin_family != AF_INET) &&
                     (addr->sin_family != AF_IMPLINK)) ||
                    (addr->sin_addr.s_addr &&
                if ((ifnet == 0) ||
                    ((addr->sin_family != AF_INET) &&
                     (addr->sin_family != AF_IMPLINK)) ||
                    (addr->sin_addr.s_addr &&
-                    ifa_ifwithaddr((struct sockaddr *)addr) == 0))
-                       return (EADDRNOTAVAIL);
-               rp->rinp_rcb.rcb_laddr = (struct sockaddr *)&rp->rinp_laddr;
-               rp->rinp_laddr = *addr;
-               return (0);
+                    ifa_ifwithaddr((struct sockaddr *)addr) == 0)) {
+                       error = EADDRNOTAVAIL;
+                       break;
+               }
+               inp->inp_laddr = addr->sin_addr;
+               break;
            }
        case PRU_CONNECT:
            {
                struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
 
            }
        case PRU_CONNECT:
            {
                struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
 
-               if (nam->m_len != sizeof(*addr))
-                       return (EINVAL);
-               if (ifnet == 0)
-                       return (EADDRNOTAVAIL);
+               if (nam->m_len != sizeof(*addr)) {
+                       error = EINVAL;
+                       break;
+               }
+               if (ifnet == 0) {
+                       error = EADDRNOTAVAIL;
+                       break;
+               }
                if ((addr->sin_family != AF_INET) &&
                if ((addr->sin_family != AF_INET) &&
-                    (addr->sin_family != AF_IMPLINK))
-                       return (EAFNOSUPPORT);
-               rp->rinp_rcb.rcb_faddr = (struct sockaddr *)&rp->rinp_faddr;
-               rp->rinp_faddr = *addr;
+                    (addr->sin_family != AF_IMPLINK)) {
+                       error = EAFNOSUPPORT;
+                       break;
+               }
+               inp->inp_faddr = addr->sin_addr;
                soisconnected(so);
                soisconnected(so);
-               return (0);
+               break;
            }
            }
-       }
-       error =  raw_usrreq(so, req, m, nam, rights, control);
 
 
-       if (error && (req == PRU_ATTACH) && so->so_pcb)
-               free(so->so_pcb, M_PCB);
+       case PRU_CONNECT2:
+               error = EOPNOTSUPP;
+               break;
+
+       /*
+        * Mark the connection as being incapable of further input.
+        */
+       case PRU_SHUTDOWN:
+               socantsendmore(so);
+               break;
+
+       /*
+        * Ship a packet out.  The appropriate raw output
+        * routine handles any massaging necessary.
+        */
+       case PRU_SEND:
+           {
+               register u_long dst;
+
+               if (so->so_state & SS_ISCONNECTED) {
+                       if (nam) {
+                               error = EISCONN;
+                               break;
+                       }
+                       dst = inp->inp_faddr.s_addr;
+               } else {
+                       if (nam == NULL) {
+                               error = ENOTCONN;
+                               break;
+                       }
+                       dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+               }
+               error = rip_output(m, so, dst);
+               m = NULL;
+               break;
+           }
+
+       case PRU_SENSE:
+               /*
+                * stat: don't bother with a blocksize.
+                */
+               return (0);
+
+       /*
+        * Not supported.
+        */
+       case PRU_RCVOOB:
+       case PRU_RCVD:
+       case PRU_LISTEN:
+       case PRU_ACCEPT:
+       case PRU_SENDOOB:
+               error = EOPNOTSUPP;
+               break;
+
+       case PRU_SOCKADDR:
+               in_setsockaddr(inp, nam);
+               break;
+
+       case PRU_PEERADDR:
+               in_setpeeraddr(inp, nam);
+               break;
+
+       default:
+               panic("rip_usrreq");
+       }
+       if (m != NULL)
+               m_freem(m);
        return (error);
 }
        return (error);
 }