BSD 4_4 release
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
index 1ae0872..32c0529 100644 (file)
@@ -1,68 +1,83 @@
 /*
 /*
- * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *     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.
  *
  *
- *     @(#)udp_usrreq.c        7.18 (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.
+ *
+ *     @(#)udp_usrreq.c        8.1 (Berkeley) 6/10/93
  */
 
  */
 
-#include "param.h"
-#include "user.h"
-#include "malloc.h"
-#include "mbuf.h"
-#include "protosw.h"
-#include "socket.h"
-#include "socketvar.h"
-#include "errno.h"
-#include "stat.h"
-
-#include "../net/if.h"
-#include "../net/route.h"
-
-#include "in.h"
-#include "in_systm.h"
-#include "ip.h"
-#include "in_pcb.h"
-#include "ip_var.h"
-#include "ip_icmp.h"
-#include "udp.h"
-#include "udp_var.h"
-
-struct inpcb *udp_last_inpcb = &udb;
+#include <sys/param.h>
+#include <sys/malloc.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 <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
 
 /*
  * UDP protocol implementation.
  * Per RFC 768, August, 1980.
  */
 
 /*
  * UDP protocol implementation.
  * Per RFC 768, August, 1980.
  */
-udp_init()
-{
-
-       udb.inp_next = udb.inp_prev = &udb;
-}
-
 #ifndef        COMPAT_42
 int    udpcksum = 1;
 #else
 int    udpcksum = 0;           /* XXX */
 #endif
 #ifndef        COMPAT_42
 int    udpcksum = 1;
 #else
 int    udpcksum = 0;           /* XXX */
 #endif
-int    udp_ttl = UDP_TTL;
 
 struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
 
 struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
+struct inpcb *udp_last_inpcb = &udb;
+
+static void udp_detach __P((struct inpcb *));
+static void udp_notify __P((struct inpcb *, int));
+static struct mbuf *udp_saveopt __P((caddr_t, int, int));
+
+void
+udp_init()
+{
+       udb.inp_next = udb.inp_prev = &udb;
+}
 
 
+void
 udp_input(m, iphlen)
        register struct mbuf *m;
        int iphlen;
 udp_input(m, iphlen)
        register struct mbuf *m;
        int iphlen;
@@ -134,6 +149,95 @@ udp_input(m, iphlen)
                }
        }
 
                }
        }
 
+       if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
+           in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
+               struct socket *last;
+               /*
+                * Deliver a multicast or broadcast datagram to *all* sockets
+                * for which the local and remote addresses and ports match
+                * those of the incoming datagram.  This allows more than
+                * one process to receive multi/broadcasts on the same port.
+                * (This really ought to be done for unicast datagrams as
+                * well, but that would cause problems with existing
+                * applications that open both address-specific sockets and
+                * a wildcard socket listening to the same port -- they would
+                * end up receiving duplicates of every unicast datagram.
+                * Those applications open the multiple sockets to overcome an
+                * inadequacy of the UDP socket interface, but for backwards
+                * compatibility we avoid the problem here rather than
+                * fixing the interface.  Maybe 4.5BSD will remedy this?)
+                */
+
+               /*
+                * Construct sockaddr format source address.
+                */
+               udp_in.sin_port = uh->uh_sport;
+               udp_in.sin_addr = ip->ip_src;
+               m->m_len -= sizeof (struct udpiphdr);
+               m->m_data += sizeof (struct udpiphdr);
+               /*
+                * Locate pcb(s) for datagram.
+                * (Algorithm copied from raw_intr().)
+                */
+               last = NULL;
+               for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
+                       if (inp->inp_lport != uh->uh_dport)
+                               continue;
+                       if (inp->inp_laddr.s_addr != INADDR_ANY) {
+                               if (inp->inp_laddr.s_addr !=
+                                   ip->ip_dst.s_addr)
+                                       continue;
+                       }
+                       if (inp->inp_faddr.s_addr != INADDR_ANY) {
+                               if (inp->inp_faddr.s_addr !=
+                                   ip->ip_src.s_addr ||
+                                   inp->inp_fport != uh->uh_sport)
+                                       continue;
+                       }
+
+                       if (last != NULL) {
+                               struct mbuf *n;
+
+                               if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+                                       if (sbappendaddr(&last->so_rcv,
+                                               (struct sockaddr *)&udp_in,
+                                               n, (struct mbuf *)0) == 0) {
+                                               m_freem(n);
+                                               udpstat.udps_fullsock++;
+                                       } else
+                                               sorwakeup(last);
+                               }
+                       }
+                       last = inp->inp_socket;
+                       /*
+                        * Don't look for additional matches if this one does
+                        * not have either the SO_REUSEPORT or SO_REUSEADDR
+                        * socket options set.  This heuristic avoids searching
+                        * through all pcbs in the common case of a non-shared
+                        * port.  It * assumes that an application will never
+                        * clear these options after setting them.
+                        */
+                       if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
+                               break;
+               }
+
+               if (last == NULL) {
+                       /*
+                        * No matching pcb found; discard datagram.
+                        * (No need to send an ICMP Port Unreachable
+                        * for a broadcast or multicast datgram.)
+                        */
+                       udpstat.udps_noportbcast++;
+                       goto bad;
+               }
+               if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
+                    m, (struct mbuf *)0) == 0) {
+                       udpstat.udps_fullsock++;
+                       goto bad;
+               }
+               sorwakeup(last);
+               return;
+       }
        /*
         * Locate pcb for datagram.
         */
        /*
         * Locate pcb for datagram.
         */
@@ -149,15 +253,10 @@ udp_input(m, iphlen)
                udpstat.udpps_pcbcachemiss++;
        }
        if (inp == 0) {
                udpstat.udpps_pcbcachemiss++;
        }
        if (inp == 0) {
-               /* don't send ICMP response for broadcast packet */
                udpstat.udps_noport++;
                udpstat.udps_noport++;
-               if (m->m_flags & M_BCAST) {
-                       udpstat.udps_noportbcast++;
-                       goto bad;
-               }
                *ip = save_ip;
                ip->ip_len += iphlen;
                *ip = save_ip;
                ip->ip_len += iphlen;
-               icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
+               icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
                return;
        }
 
                return;
        }
 
@@ -169,7 +268,6 @@ udp_input(m, iphlen)
        udp_in.sin_addr = ip->ip_src;
        if (inp->inp_flags & INP_CONTROLOPTS) {
                struct mbuf **mp = &opts;
        udp_in.sin_addr = ip->ip_src;
        if (inp->inp_flags & INP_CONTROLOPTS) {
                struct mbuf **mp = &opts;
-               struct mbuf *udp_saveopt();
 
                if (inp->inp_flags & INP_RECVDSTADDR) {
                        *mp = udp_saveopt((caddr_t) &ip->ip_dst,
 
                if (inp->inp_flags & INP_RECVDSTADDR) {
                        *mp = udp_saveopt((caddr_t) &ip->ip_dst,
@@ -227,7 +325,7 @@ udp_saveopt(p, size, type)
        if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
                return ((struct mbuf *) NULL);
        cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
        if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
                return ((struct mbuf *) NULL);
        cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
-       bcopy(p, (caddr_t)cp + size, size);
+       bcopy(p, CMSG_DATA(cp), size);
        size += sizeof(*cp);
        m->m_len = size;
        cp->cmsg_len = size;
        size += sizeof(*cp);
        m->m_len = size;
        cp->cmsg_len = size;
@@ -240,15 +338,17 @@ udp_saveopt(p, size, type)
  * Notify a udp user of an asynchronous error;
  * just wake up so that he can collect error status.
  */
  * Notify a udp user of an asynchronous error;
  * just wake up so that he can collect error status.
  */
+static void
 udp_notify(inp, errno)
        register struct inpcb *inp;
 udp_notify(inp, errno)
        register struct inpcb *inp;
+       int errno;
 {
 {
-
        inp->inp_socket->so_error = errno;
        sorwakeup(inp->inp_socket);
        sowwakeup(inp->inp_socket);
 }
 
        inp->inp_socket->so_error = errno;
        sorwakeup(inp->inp_socket);
        sowwakeup(inp->inp_socket);
 }
 
+void
 udp_ctlinput(cmd, sa, ip)
        int cmd;
        struct sockaddr *sa;
 udp_ctlinput(cmd, sa, ip)
        int cmd;
        struct sockaddr *sa;
@@ -258,7 +358,8 @@ udp_ctlinput(cmd, sa, ip)
        extern struct in_addr zeroin_addr;
        extern u_char inetctlerrmap[];
 
        extern struct in_addr zeroin_addr;
        extern u_char inetctlerrmap[];
 
-       if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
+       if (!PRC_IS_REDIRECT(cmd) &&
+           ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
                return;
        if (ip) {
                uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
                return;
        if (ip) {
                uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
@@ -268,6 +369,7 @@ udp_ctlinput(cmd, sa, ip)
                in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
 }
 
                in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
 }
 
+int
 udp_output(inp, m, addr, control)
        register struct inpcb *inp;
        register struct mbuf *m;
 udp_output(inp, m, addr, control)
        register struct inpcb *inp;
        register struct mbuf *m;
@@ -306,7 +408,11 @@ udp_output(inp, m, addr, control)
         * Calculate data length and get a mbuf
         * for UDP and IP headers.
         */
         * Calculate data length and get a mbuf
         * for UDP and IP headers.
         */
-       M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
+       M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
+       if (m == 0) {
+               error = ENOBUFS;
+               goto release;
+       }
 
        /*
         * Fill in mbuf with extended UDP header
 
        /*
         * Fill in mbuf with extended UDP header
@@ -336,7 +442,8 @@ udp_output(inp, m, addr, control)
        ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
        udpstat.udps_opackets++;
        error = ip_output(m, inp->inp_options, &inp->inp_route,
        ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
        udpstat.udps_opackets++;
        error = ip_output(m, inp->inp_options, &inp->inp_route,
-           inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST));
+           inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
+           inp->inp_moptions);
 
        if (addr) {
                in_pcbdisconnect(inp);
 
        if (addr) {
                in_pcbdisconnect(inp);
@@ -355,6 +462,7 @@ u_long      udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
                                        /* 40 1K datagrams */
 
 /*ARGSUSED*/
                                        /* 40 1K datagrams */
 
 /*ARGSUSED*/
+int
 udp_usrreq(so, req, m, addr, control)
        struct socket *so;
        int req;
 udp_usrreq(so, req, m, addr, control)
        struct socket *so;
        int req;
@@ -390,7 +498,7 @@ udp_usrreq(so, req, m, addr, control)
                error = soreserve(so, udp_sendspace, udp_recvspace);
                if (error)
                        break;
                error = soreserve(so, udp_sendspace, udp_recvspace);
                if (error)
                        break;
-               ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = udp_ttl;
+               ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
                break;
 
        case PRU_DETACH:
                break;
 
        case PRU_DETACH:
@@ -491,6 +599,7 @@ release:
        return (error);
 }
 
        return (error);
 }
 
+static void
 udp_detach(inp)
        struct inpcb *inp;
 {
 udp_detach(inp)
        struct inpcb *inp;
 {
@@ -501,3 +610,29 @@ udp_detach(inp)
        in_pcbdetach(inp);
        splx(s);
 }
        in_pcbdetach(inp);
        splx(s);
 }
+
+/*
+ * Sysctl for udp variables.
+ */
+udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+       int *name;
+       u_int namelen;
+       void *oldp;
+       size_t *oldlenp;
+       void *newp;
+       size_t newlen;
+{
+       extern int ip_ttl;
+
+       /* all sysctl names at this level are terminal */
+       if (namelen != 1)
+               return (ENOTDIR);
+
+       switch (name[0]) {
+       case UDPCTL_CHECKSUM:
+               return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
+       default:
+               return (ENOPROTOOPT);
+       }
+       /* NOTREACHED */
+}