trailing comment after #else or #endif
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
index 41f00db..c957a43 100644 (file)
@@ -1,25 +1,34 @@
-/*     udp_usrreq.c    6.3     83/11/14        */
-
-#include "../h/param.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/mbuf.h"
-#include "../h/protosw.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../h/errno.h"
-
-#include "../net/if.h"
-#include "../net/route.h"
-
-#include "../netinet/in.h"
-#include "../netinet/in_pcb.h"
-#include "../netinet/in_systm.h"
-#include "../netinet/ip.h"
-#include "../netinet/ip_var.h"
-#include "../netinet/ip_icmp.h"
-#include "../netinet/udp.h"
-#include "../netinet/udp_var.h"
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)udp_usrreq.c        7.31 (Berkeley) %G%
+ */
+
+#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>
+
+struct inpcb *udp_last_inpcb = &udb;
 
 /*
  * UDP protocol implementation.
 
 /*
  * UDP protocol implementation.
  */
 udp_init()
 {
  */
 udp_init()
 {
-
        udb.inp_next = udb.inp_prev = &udb;
 }
 
        udb.inp_next = udb.inp_prev = &udb;
 }
 
-int    udpcksum = 0;
-struct sockaddr_in udp_in = { AF_INET };
+#ifndef        COMPAT_42
+int    udpcksum = 1;
+#else
+int    udpcksum = 0;           /* XXX */
+#endif
 
 
-udp_input(m0)
-       struct mbuf *m0;
+struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
+
+udp_input(m, iphlen)
+       register struct mbuf *m;
+       int iphlen;
 {
 {
-       register struct udpiphdr *ui;
+       register struct ip *ip;
+       register struct udphdr *uh;
        register struct inpcb *inp;
        register struct inpcb *inp;
-       register struct mbuf *m;
+       struct mbuf *opts = 0;
        int len;
        int len;
+       struct ip save_ip;
+
+       udpstat.udps_ipackets++;
+
+       /*
+        * Strip IP options, if any; should skip this,
+        * make available to user, and use on returned packets,
+        * but we don't yet have a way to check the checksum
+        * with options still present.
+        */
+       if (iphlen > sizeof (struct ip)) {
+               ip_stripoptions(m, (struct mbuf *)0);
+               iphlen = sizeof(struct ip);
+       }
 
        /*
         * Get IP and UDP header together in first mbuf.
         */
 
        /*
         * Get IP and UDP header together in first mbuf.
         */
-       m = m0;
-       if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct udpiphdr)) &&
-           (m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
-               udpstat.udps_hdrops++;
-               return;
+       ip = mtod(m, struct ip *);
+       if (m->m_len < iphlen + sizeof(struct udphdr)) {
+               if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
+                       udpstat.udps_hdrops++;
+                       return;
+               }
+               ip = mtod(m, struct ip *);
        }
        }
-       ui = mtod(m, struct udpiphdr *);
-       if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
-               ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
+       uh = (struct udphdr *)((caddr_t)ip + iphlen);
 
        /*
         * Make mbuf data length reflect UDP length.
         * If not enough data to reflect UDP length, drop.
         */
 
        /*
         * Make mbuf data length reflect UDP length.
         * If not enough data to reflect UDP length, drop.
         */
-       len = ntohs((u_short)ui->ui_ulen);
-       if (((struct ip *)ui)->ip_len != len) {
-               if (len > ((struct ip *)ui)->ip_len) {
+       len = ntohs((u_short)uh->uh_ulen);
+       if (ip->ip_len != len) {
+               if (len > ip->ip_len) {
                        udpstat.udps_badlen++;
                        goto bad;
                }
                        udpstat.udps_badlen++;
                        goto bad;
                }
-               m_adj(m, len - ((struct ip *)ui)->ip_len);
-               /* (struct ip *)ui->ip_len = len; */
+               m_adj(m, len - ip->ip_len);
+               /* ip->ip_len = len; */
        }
        }
+       /*
+        * Save a copy of the IP header in case we want restore it
+        * for sending an ICMP error message in response.
+        */
+       save_ip = *ip;
 
        /*
         * Checksum extended UDP header and data.
         */
 
        /*
         * Checksum extended UDP header and data.
         */
-       if (udpcksum && ui->ui_sum) {
-               ui->ui_next = ui->ui_prev = 0;
-               ui->ui_x1 = 0;
-               ui->ui_len = htons((u_short)len);
-               if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
+       if (udpcksum && uh->uh_sum) {
+               ((struct ipovly *)ip)->ih_next = 0;
+               ((struct ipovly *)ip)->ih_prev = 0;
+               ((struct ipovly *)ip)->ih_x1 = 0;
+               ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+               if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
                        udpstat.udps_badsum++;
                        m_freem(m);
                        return;
                }
        }
 
                        udpstat.udps_badsum++;
                        m_freem(m);
                        return;
                }
        }
 
+       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 the SO_REUSEPORT socket option 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
+                        * the SO_REUSEPORT option after setting it.
+                        */
+                       if ((last->so_options & SO_REUSEPORT) == 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.
         */
-       inp = in_pcblookup(&udb,
-           ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
-               INPLOOKUP_WILDCARD);
+       inp = udp_last_inpcb;
+       if (inp->inp_lport != uh->uh_dport ||
+           inp->inp_fport != uh->uh_sport ||
+           inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
+           inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
+               inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
+                   ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
+               if (inp)
+                       udp_last_inpcb = inp;
+               udpstat.udpps_pcbcachemiss++;
+       }
        if (inp == 0) {
        if (inp == 0) {
-               /* don't send ICMP response for broadcast packet */
-               if (in_lnaof(ui->ui_dst) == INADDR_ANY)
-                       goto bad;
-               icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
+               udpstat.udps_noport++;
+               *ip = save_ip;
+               ip->ip_len += iphlen;
+               icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
                return;
        }
 
                return;
        }
 
@@ -101,86 +233,158 @@ udp_input(m0)
         * Construct sockaddr format source address.
         * Stuff source address and datagram in user buffer.
         */
         * Construct sockaddr format source address.
         * Stuff source address and datagram in user buffer.
         */
-       udp_in.sin_port = ui->ui_sport;
-       udp_in.sin_addr = ui->ui_src;
-       m->m_len -= sizeof (struct udpiphdr);
-       m->m_off += sizeof (struct udpiphdr);
+       udp_in.sin_port = uh->uh_sport;
+       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,
+                           sizeof(struct in_addr), IP_RECVDSTADDR);
+                       if (*mp)
+                               mp = &(*mp)->m_next;
+               }
+#ifdef notyet
+               /* options were tossed above */
+               if (inp->inp_flags & INP_RECVOPTS) {
+                       *mp = udp_saveopt((caddr_t) opts_deleted_above,
+                           sizeof(struct in_addr), IP_RECVOPTS);
+                       if (*mp)
+                               mp = &(*mp)->m_next;
+               }
+               /* ip_srcroute doesn't do what we want here, need to fix */
+               if (inp->inp_flags & INP_RECVRETOPTS) {
+                       *mp = udp_saveopt((caddr_t) ip_srcroute(),
+                           sizeof(struct in_addr), IP_RECVRETOPTS);
+                       if (*mp)
+                               mp = &(*mp)->m_next;
+               }
+#endif
+       }
+       iphlen += sizeof(struct udphdr);
+       m->m_len -= iphlen;
+       m->m_pkthdr.len -= iphlen;
+       m->m_data += iphlen;
        if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
        if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
-           m, (struct mbuf *)0) == 0)
+           m, opts) == 0) {
+               udpstat.udps_fullsock++;
                goto bad;
                goto bad;
+       }
        sorwakeup(inp->inp_socket);
        return;
 bad:
        m_freem(m);
        sorwakeup(inp->inp_socket);
        return;
 bad:
        m_freem(m);
+       if (opts)
+               m_freem(opts);
 }
 
 }
 
-udp_abort(inp)
-       struct inpcb *inp;
+/*
+ * Create a "control" mbuf containing the specified data
+ * with the specified type for presentation with a datagram.
+ */
+struct mbuf *
+udp_saveopt(p, size, type)
+       caddr_t p;
+       register int size;
+       int type;
 {
 {
-       struct socket *so = inp->inp_socket;
+       register struct cmsghdr *cp;
+       struct mbuf *m;
+
+       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 + 1), size);
+       size += sizeof(*cp);
+       m->m_len = size;
+       cp->cmsg_len = size;
+       cp->cmsg_level = IPPROTO_IP;
+       cp->cmsg_type = type;
+       return (m);
+}
 
 
-       in_pcbdisconnect(inp);
-       soisdisconnected(so);
+/*
+ * Notify a udp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+udp_notify(inp, errno)
+       register struct inpcb *inp;
+       int errno;
+{
+       inp->inp_socket->so_error = errno;
+       sorwakeup(inp->inp_socket);
+       sowwakeup(inp->inp_socket);
 }
 
 }
 
-udp_ctlinput(cmd, arg)
+udp_ctlinput(cmd, sa, ip)
        int cmd;
        int cmd;
-       caddr_t arg;
+       struct sockaddr *sa;
+       register struct ip *ip;
 {
 {
-       struct in_addr *sin;
+       register struct udphdr *uh;
+       extern struct in_addr zeroin_addr;
        extern u_char inetctlerrmap[];
 
        extern u_char inetctlerrmap[];
 
-       if (cmd < 0 || cmd > PRC_NCMDS)
+       if (!PRC_IS_REDIRECT(cmd) &&
+           ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
                return;
                return;
-       switch (cmd) {
-
-       case PRC_ROUTEDEAD:
-               break;
-
-       case PRC_QUENCH:
-               break;
-
-       /* these are handled by ip */
-       case PRC_IFDOWN:
-       case PRC_HOSTDEAD:
-       case PRC_HOSTUNREACH:
-               break;
-
-       default:
-               sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
-               in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
-       }
+       if (ip) {
+               uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+               in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
+                       cmd, udp_notify);
+       } else
+               in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
 }
 
 }
 
-udp_output(inp, m0)
-       struct inpcb *inp;
-       struct mbuf *m0;
-{
+udp_output(inp, m, addr, control)
+       register struct inpcb *inp;
        register struct mbuf *m;
        register struct mbuf *m;
+       struct mbuf *addr, *control;
+{
        register struct udpiphdr *ui;
        register struct udpiphdr *ui;
-       register struct socket *so;
-       register int len = 0;
-       int flags;
+       register int len = m->m_pkthdr.len;
+       struct in_addr laddr;
+       int s, error = 0;
+
+       if (control)
+               m_freem(control);               /* XXX */
 
 
+       if (addr) {
+               laddr = inp->inp_laddr;
+               if (inp->inp_faddr.s_addr != INADDR_ANY) {
+                       error = EISCONN;
+                       goto release;
+               }
+               /*
+                * Must block input while temporarily connected.
+                */
+               s = splnet();
+               error = in_pcbconnect(inp, addr);
+               if (error) {
+                       splx(s);
+                       goto release;
+               }
+       } else {
+               if (inp->inp_faddr.s_addr == INADDR_ANY) {
+                       error = ENOTCONN;
+                       goto release;
+               }
+       }
        /*
         * Calculate data length and get a mbuf
         * for UDP and IP headers.
         */
        /*
         * Calculate data length and get a mbuf
         * for UDP and IP headers.
         */
-       for (m = m0; m; m = m->m_next)
-               len += m->m_len;
-       m = m_get(M_DONTWAIT, MT_HEADER);
+       M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
        if (m == 0) {
        if (m == 0) {
-               m_freem(m0);
-               return (ENOBUFS);
+               error = ENOBUFS;
+               goto release;
        }
 
        /*
         * Fill in mbuf with extended UDP header
         * and addresses and length put into network format.
         */
        }
 
        /*
         * Fill in mbuf with extended UDP header
         * and addresses and length put into network format.
         */
-       m->m_off = MMAXOFF - sizeof (struct udpiphdr);
-       m->m_len = sizeof (struct udpiphdr);
-       m->m_next = m0;
        ui = mtod(m, struct udpiphdr *);
        ui->ui_next = ui->ui_prev = 0;
        ui->ui_x1 = 0;
        ui = mtod(m, struct udpiphdr *);
        ui->ui_next = ui->ui_prev = 0;
        ui->ui_x1 = 0;
@@ -196,32 +400,55 @@ udp_output(inp, m0)
         * Stuff checksum and output datagram.
         */
        ui->ui_sum = 0;
         * Stuff checksum and output datagram.
         */
        ui->ui_sum = 0;
-       if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
-               ui->ui_sum = -1;
+       if (udpcksum) {
+           if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
+               ui->ui_sum = 0xffff;
+       }
        ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
        ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
-       ((struct ip *)ui)->ip_ttl = MAXTTL;
-       so = inp->inp_socket;
-       flags = (so->so_options & SO_DONTROUTE) | (so->so_state & SS_PRIV);
-       return (ip_output(m, (struct mbuf *)0, (struct route *)0, flags));
+       ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */
+       ((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_moptions);
+
+       if (addr) {
+               in_pcbdisconnect(inp);
+               inp->inp_laddr = laddr;
+               splx(s);
+       }
+       return (error);
+
+release:
+       m_freem(m);
+       return (error);
 }
 
 }
 
+u_long udp_sendspace = 9216;           /* really max datagram size */
+u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
+                                       /* 40 1K datagrams */
+
 /*ARGSUSED*/
 /*ARGSUSED*/
-udp_usrreq(so, req, m, nam, rights)
+udp_usrreq(so, req, m, addr, control)
        struct socket *so;
        int req;
        struct socket *so;
        int req;
-       struct mbuf *m, *nam, *rights;
+       struct mbuf *m, *addr, *control;
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
+       int s;
 
 
-       if (rights && rights->m_len) {
-               error = EINVAL;
-               goto release;
-       }
+       if (req == PRU_CONTROL)
+               return (in_control(so, (int)m, (caddr_t)addr,
+                       (struct ifnet *)control));
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
                goto release;
        }
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
                goto release;
        }
+       /*
+        * Note: need to block udp_input while changing
+        * the udp pcb queue and/or pcb addresses.
+        */
        switch (req) {
 
        case PRU_ATTACH:
        switch (req) {
 
        case PRU_ATTACH:
@@ -229,24 +456,25 @@ udp_usrreq(so, req, m, nam, rights)
                        error = EINVAL;
                        break;
                }
                        error = EINVAL;
                        break;
                }
+               s = splnet();
                error = in_pcballoc(so, &udb);
                error = in_pcballoc(so, &udb);
+               splx(s);
                if (error)
                        break;
                if (error)
                        break;
-               error = soreserve(so, 2048, 2048);
+               error = soreserve(so, udp_sendspace, udp_recvspace);
                if (error)
                        break;
                if (error)
                        break;
+               ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
                break;
 
        case PRU_DETACH:
                break;
 
        case PRU_DETACH:
-               if (inp == NULL) {
-                       error = ENOTCONN;
-                       break;
-               }
-               in_pcbdetach(inp);
+               udp_detach(inp);
                break;
 
        case PRU_BIND:
                break;
 
        case PRU_BIND:
-               error = in_pcbbind(inp, nam);
+               s = splnet();
+               error = in_pcbbind(inp, addr);
+               splx(s);
                break;
 
        case PRU_LISTEN:
                break;
 
        case PRU_LISTEN:
@@ -258,7 +486,9 @@ udp_usrreq(so, req, m, nam, rights)
                        error = EISCONN;
                        break;
                }
                        error = EISCONN;
                        break;
                }
-               error = in_pcbconnect(inp, nam);
+               s = splnet();
+               error = in_pcbconnect(inp, addr);
+               splx(s);
                if (error == 0)
                        soisconnected(so);
                break;
                if (error == 0)
                        soisconnected(so);
                break;
@@ -276,66 +506,39 @@ udp_usrreq(so, req, m, nam, rights)
                        error = ENOTCONN;
                        break;
                }
                        error = ENOTCONN;
                        break;
                }
+               s = splnet();
                in_pcbdisconnect(inp);
                in_pcbdisconnect(inp);
-               soisdisconnected(so);
+               inp->inp_laddr.s_addr = INADDR_ANY;
+               splx(s);
+               so->so_state &= ~SS_ISCONNECTED;                /* XXX */
                break;
 
        case PRU_SHUTDOWN:
                socantsendmore(so);
                break;
 
                break;
 
        case PRU_SHUTDOWN:
                socantsendmore(so);
                break;
 
-       case PRU_SEND: {
-               struct in_addr laddr;
-
-               if (nam) {
-                       laddr = inp->inp_laddr;
-                       if (inp->inp_faddr.s_addr != INADDR_ANY) {
-                               error = EISCONN;
-                               break;
-                       }
-                       error = in_pcbconnect(inp, nam);
-                       if (error)
-                               break;
-               } else {
-                       if (inp->inp_faddr.s_addr == INADDR_ANY) {
-                               error = ENOTCONN;
-                               break;
-                       }
-               }
-               error = udp_output(inp, m);
-               m = NULL;
-               if (nam) {
-                       in_pcbdisconnect(inp);
-                       inp->inp_laddr = laddr;
-               }
-               }
-               break;
+       case PRU_SEND:
+               return (udp_output(inp, m, addr, control));
 
        case PRU_ABORT:
 
        case PRU_ABORT:
-               in_pcbdetach(inp);
-               sofree(so);
                soisdisconnected(so);
                soisdisconnected(so);
+               udp_detach(inp);
                break;
 
        case PRU_SOCKADDR:
                break;
 
        case PRU_SOCKADDR:
-               in_setsockaddr(inp, nam);
+               in_setsockaddr(inp, addr);
                break;
 
        case PRU_PEERADDR:
                break;
 
        case PRU_PEERADDR:
-               in_setpeeraddr(inp, nam);
-               break;
-
-       case PRU_CONTROL:
-               m = NULL;
-               error = EOPNOTSUPP;
+               in_setpeeraddr(inp, addr);
                break;
 
        case PRU_SENSE:
                break;
 
        case PRU_SENSE:
-               m = NULL;
-               /* fall thru... */
+               /*
+                * stat: don't bother with a blocksize.
+                */
+               return (0);
 
 
-       case PRU_RCVD:
-       case PRU_RCVOOB:
        case PRU_SENDOOB:
        case PRU_FASTTIMO:
        case PRU_SLOWTIMO:
        case PRU_SENDOOB:
        case PRU_FASTTIMO:
        case PRU_SLOWTIMO:
@@ -344,11 +547,57 @@ udp_usrreq(so, req, m, nam, rights)
                error =  EOPNOTSUPP;
                break;
 
                error =  EOPNOTSUPP;
                break;
 
+       case PRU_RCVD:
+       case PRU_RCVOOB:
+               return (EOPNOTSUPP);    /* do not free mbuf's */
+
        default:
                panic("udp_usrreq");
        }
        default:
                panic("udp_usrreq");
        }
+
 release:
 release:
-       if (m != NULL)
+       if (control) {
+               printf("udp control data unexpectedly retained\n");
+               m_freem(control);
+       }
+       if (m)
                m_freem(m);
        return (error);
 }
                m_freem(m);
        return (error);
 }
+
+udp_detach(inp)
+       struct inpcb *inp;
+{
+       int s = splnet();
+
+       if (inp == udp_last_inpcb)
+               udp_last_inpcb = &udb;
+       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 */
+}