date and time created 88/12/14 15:29:27 by sklower
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
index 31bf469..0edf4fa 100644 (file)
@@ -1,25 +1,44 @@
-/*     udp_usrreq.c    4.45    83/02/16        */
+/*
+ * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)udp_usrreq.c        7.9 (Berkeley) %G%
+ */
 
 
-#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 "param.h"
+#include "dir.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 "../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"
+#include "in.h"
+#include "in_pcb.h"
+#include "in_systm.h"
+#include "ip.h"
+#include "ip_var.h"
+#include "ip_icmp.h"
+#include "udp.h"
+#include "udp_var.h"
 
 /*
  * UDP protocol implementation.
 
 /*
  * UDP protocol implementation.
@@ -31,29 +50,38 @@ udp_init()
        udb.inp_next = udb.inp_prev = &udb;
 }
 
        udb.inp_next = udb.inp_prev = &udb;
 }
 
-int    udpcksum;
+#ifndef        COMPAT_42
+int    udpcksum = 1;
+#else
+int    udpcksum = 0;           /* XXX */
+#endif
+int    udp_ttl = UDP_TTL;
+
 struct sockaddr_in udp_in = { AF_INET };
 
 struct sockaddr_in udp_in = { AF_INET };
 
-udp_input(m0)
-       struct mbuf *m0;
+udp_input(m, iphlen)
+       register struct mbuf *m;
+       int iphlen;
 {
        register struct udpiphdr *ui;
        register struct inpcb *inp;
 {
        register struct udpiphdr *ui;
        register struct inpcb *inp;
-       register struct mbuf *m;
        int len;
        int len;
+       struct ip ip;
 
        /*
         * Get IP and UDP header together in first mbuf.
 
        /*
         * Get IP and UDP header together in first mbuf.
+        * Note: IP leaves IP header 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;
-       }
        ui = mtod(m, struct udpiphdr *);
        ui = mtod(m, struct udpiphdr *);
-       if (((struct ip *)ui)->ip_hl > (sizeof (struct ip) >> 2))
-               ip_stripoptions((struct ip *)ui, (struct mbuf *)0);
+       if (iphlen > sizeof (struct ip))
+               ip_stripoptions(m, (struct mbuf *)0);
+       if (m->m_len < sizeof (struct udpiphdr)) {
+               if ((m = m_pullup(m, sizeof (struct udpiphdr))) == 0) {
+                       udpstat.udps_hdrops++;
+                       return;
+               }
+               ui = mtod(m, struct udpiphdr *);
+       }
 
        /*
         * Make mbuf data length reflect UDP length.
 
        /*
         * Make mbuf data length reflect UDP length.
@@ -65,20 +93,23 @@ udp_input(m0)
                        udpstat.udps_badlen++;
                        goto bad;
                }
                        udpstat.udps_badlen++;
                        goto bad;
                }
-               m_adj(m, ((struct ip *)ui)->ip_len - len);
-               /* (struct ip *)ui->ip_len = len; */
+               m_adj(m, len - ((struct ip *)ui)->ip_len);
+               /* ((struct ip *)ui)->ip_len = len; */
        }
        }
+       /*
+        * Save a copy of the IP header in case we want restore it for ICMP.
+        */
+       ip = *(struct ip *)ui;
 
        /*
         * Checksum extended UDP header and data.
         */
 
        /*
         * Checksum extended UDP header and data.
         */
-       if (udpcksum) {
+       if (udpcksum && ui->ui_sum) {
                ui->ui_next = ui->ui_prev = 0;
                ui->ui_x1 = 0;
                ui->ui_next = ui->ui_prev = 0;
                ui->ui_x1 = 0;
-               ui->ui_len = htons((u_short)len);
+               ui->ui_len = ui->ui_ulen;
                if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
                        udpstat.udps_badsum++;
                if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
                        udpstat.udps_badsum++;
-                       printf("udp cksum %x\n", ui->ui_sum);
                        m_freem(m);
                        return;
                }
                        m_freem(m);
                        return;
                }
@@ -92,9 +123,10 @@ udp_input(m0)
                INPLOOKUP_WILDCARD);
        if (inp == 0) {
                /* don't send ICMP response for broadcast packet */
                INPLOOKUP_WILDCARD);
        if (inp == 0) {
                /* don't send ICMP response for broadcast packet */
-               if (in_lnaof(ui->ui_dst) == INADDR_ANY)
+               if (m->m_flags & M_BCAST)
                        goto bad;
                        goto bad;
-               icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
+               *(struct ip *)ui = ip;
+               icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT);
                return;
        }
 
                return;
        }
 
@@ -105,8 +137,10 @@ udp_input(m0)
        udp_in.sin_port = ui->ui_sport;
        udp_in.sin_addr = ui->ui_src;
        m->m_len -= sizeof (struct udpiphdr);
        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);
-       if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m) == 0)
+       m->m_pkthdr.len -= sizeof (struct udpiphdr);
+       m->m_data += sizeof (struct udpiphdr);
+       if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
+           m, (struct mbuf *)0) == 0)
                goto bad;
        sorwakeup(inp->inp_socket);
        return;
                goto bad;
        sorwakeup(inp->inp_socket);
        return;
@@ -114,105 +148,116 @@ bad:
        m_freem(m);
 }
 
        m_freem(m);
 }
 
-udp_abort(inp)
-       struct inpcb *inp;
+/*
+ * Notify a udp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+udp_notify(inp)
+       register struct inpcb *inp;
 {
 {
-       struct socket *so = inp->inp_socket;
 
 
-       in_pcbdisconnect(inp);
-       soisdisconnected(so);
+       sorwakeup(inp->inp_socket);
+       sowwakeup(inp->inp_socket);
 }
 
 }
 
-udp_ctlinput(cmd, arg)
+udp_ctlinput(cmd, sa)
        int cmd;
        int cmd;
-       caddr_t arg;
+       struct sockaddr *sa;
 {
 {
-       struct in_addr *sin;
        extern u_char inetctlerrmap[];
        extern u_char inetctlerrmap[];
+       struct sockaddr_in *sin;
+       int in_rtchange();
 
 
-       if (cmd < 0 || cmd > PRC_NCMDS)
+       if ((unsigned)cmd > PRC_NCMDS)
+               return;
+       if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK)
+               return;
+       sin = (struct sockaddr_in *)sa;
+       if (sin->sin_addr.s_addr == INADDR_ANY)
                return;
                return;
-       switch (cmd) {
 
 
-       case PRC_ROUTEDEAD:
-               break;
+       switch (cmd) {
 
        case PRC_QUENCH:
                break;
 
 
        case PRC_QUENCH:
                break;
 
-       /* these are handled by ip */
-       case PRC_IFDOWN:
-       case PRC_HOSTDEAD:
-       case PRC_HOSTUNREACH:
+       case PRC_ROUTEDEAD:
+       case PRC_REDIRECT_NET:
+       case PRC_REDIRECT_HOST:
+       case PRC_REDIRECT_TOSNET:
+       case PRC_REDIRECT_TOSHOST:
+               in_pcbnotify(&udb, &sin->sin_addr, 0, in_rtchange);
                break;
 
        default:
                break;
 
        default:
-               sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
-               in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
+               if (inetctlerrmap[cmd] == 0)
+                       return;         /* XXX */
+               in_pcbnotify(&udb, &sin->sin_addr, (int)inetctlerrmap[cmd],
+                       udp_notify);
        }
 }
 
        }
 }
 
-udp_output(inp, m0)
-       struct inpcb *inp;
-       struct mbuf *m0;
-{
+udp_output(inp, m)
+       register struct inpcb *inp;
        register struct mbuf *m;
        register struct mbuf *m;
+{
        register struct udpiphdr *ui;
        register struct udpiphdr *ui;
-       register struct socket *so;
-       register int len = 0;
+       register int len = m->m_pkthdr.len;
 
        /*
         * 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);
-       if (m == 0) {
-               m_freem(m0);
-               return (ENOBUFS);
-       }
+       M_PREPEND(m, sizeof(struct udpiphdr), M_WAIT);
 
        /*
         * 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->ui_pr = IPPROTO_UDP;
        ui = mtod(m, struct udpiphdr *);
        ui->ui_next = ui->ui_prev = 0;
        ui->ui_x1 = 0;
        ui->ui_pr = IPPROTO_UDP;
-       ui->ui_len = len + sizeof (struct udphdr);
+       ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
        ui->ui_src = inp->inp_laddr;
        ui->ui_dst = inp->inp_faddr;
        ui->ui_sport = inp->inp_lport;
        ui->ui_dport = inp->inp_fport;
        ui->ui_src = inp->inp_laddr;
        ui->ui_dst = inp->inp_faddr;
        ui->ui_sport = inp->inp_lport;
        ui->ui_dport = inp->inp_fport;
-       ui->ui_ulen = htons((u_short)ui->ui_len);
+       ui->ui_ulen = ui->ui_len;
 
        /*
         * Stuff checksum and output datagram.
         */
        ui->ui_sum = 0;
 
        /*
         * Stuff checksum and output datagram.
         */
        ui->ui_sum = 0;
-       ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len);
+       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;
-       return (ip_output(m, (struct mbuf *)0,
-           (so->so_options & SO_DONTROUTE) ? &routetoif : (struct route *)0,
-           so->so_state & SS_PRIV));
+       ((struct ip *)ui)->ip_ttl = udp_ttl;
+       return (ip_output(m, inp->inp_options, &inp->inp_route,
+           inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
 }
 
 }
 
+u_long udp_sendspace = 2048;           /* really max datagram size */
+u_long udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
+
 /*ARGSUSED*/
 /*ARGSUSED*/
-udp_usrreq(so, req, m, nam)
+udp_usrreq(so, req, m, nam, rights, control)
        struct socket *so;
        int req;
        struct socket *so;
        int req;
-       struct mbuf *m, *nam;
+       struct mbuf *m, *nam, *rights, *control;
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
 
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
 
+       if (req == PRU_CONTROL)
+               return (in_control(so, (int)m, (caddr_t)nam,
+                       (struct ifnet *)rights));
+       if (rights && rights->m_len) {
+               error = EINVAL;
+               goto release;
+       }
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
                goto release;
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
                goto release;
@@ -227,16 +272,12 @@ udp_usrreq(so, req, m, nam)
                error = in_pcballoc(so, &udb);
                if (error)
                        break;
                error = in_pcballoc(so, &udb);
                if (error)
                        break;
-               error = soreserve(so, 2048, 2048);
+               error = soreserve(so, udp_sendspace, udp_recvspace);
                if (error)
                        break;
                break;
 
        case PRU_DETACH:
                if (error)
                        break;
                break;
 
        case PRU_DETACH:
-               if (inp == NULL) {
-                       error = ENOTCONN;
-                       break;
-               }
                in_pcbdetach(inp);
                break;
 
                in_pcbdetach(inp);
                break;
 
@@ -258,6 +299,10 @@ udp_usrreq(so, req, m, nam)
                        soisconnected(so);
                break;
 
                        soisconnected(so);
                break;
 
+       case PRU_CONNECT2:
+               error = EOPNOTSUPP;
+               break;
+
        case PRU_ACCEPT:
                error = EOPNOTSUPP;
                break;
        case PRU_ACCEPT:
                error = EOPNOTSUPP;
                break;
@@ -268,7 +313,7 @@ udp_usrreq(so, req, m, nam)
                        break;
                }
                in_pcbdisconnect(inp);
                        break;
                }
                in_pcbdisconnect(inp);
-               soisdisconnected(so);
+               so->so_state &= ~SS_ISCONNECTED;                /* XXX */
                break;
 
        case PRU_SHUTDOWN:
                break;
 
        case PRU_SHUTDOWN:
@@ -277,6 +322,7 @@ udp_usrreq(so, req, m, nam)
 
        case PRU_SEND: {
                struct in_addr laddr;
 
        case PRU_SEND: {
                struct in_addr laddr;
+               int s;
 
                if (nam) {
                        laddr = inp->inp_laddr;
 
                if (nam) {
                        laddr = inp->inp_laddr;
@@ -284,9 +330,15 @@ udp_usrreq(so, req, m, nam)
                                error = EISCONN;
                                break;
                        }
                                error = EISCONN;
                                break;
                        }
+                       /*
+                        * Must block input while temporarily connected.
+                        */
+                       s = splnet();
                        error = in_pcbconnect(inp, nam);
                        error = in_pcbconnect(inp, nam);
-                       if (error)
+                       if (error) {
+                               splx(s);
                                break;
                                break;
+                       }
                } else {
                        if (inp->inp_faddr.s_addr == INADDR_ANY) {
                                error = ENOTCONN;
                } else {
                        if (inp->inp_faddr.s_addr == INADDR_ANY) {
                                error = ENOTCONN;
@@ -298,24 +350,42 @@ udp_usrreq(so, req, m, nam)
                if (nam) {
                        in_pcbdisconnect(inp);
                        inp->inp_laddr = laddr;
                if (nam) {
                        in_pcbdisconnect(inp);
                        inp->inp_laddr = laddr;
+                       splx(s);
                }
                }
                break;
 
        case PRU_ABORT:
                }
                }
                break;
 
        case PRU_ABORT:
-               in_pcbdetach(inp);
-               sofree(so);
                soisdisconnected(so);
                soisdisconnected(so);
-               break;
-
-       case PRU_CONTROL:
-               error =  EOPNOTSUPP;
+               in_pcbdetach(inp);
                break;
 
        case PRU_SOCKADDR:
                in_setsockaddr(inp, nam);
                break;
 
                break;
 
        case PRU_SOCKADDR:
                in_setsockaddr(inp, nam);
                break;
 
+       case PRU_PEERADDR:
+               in_setpeeraddr(inp, nam);
+               break;
+
+       case PRU_SENSE:
+               /*
+                * stat: don't bother with a blocksize.
+                */
+               return (0);
+
+       case PRU_SENDOOB:
+       case PRU_FASTTIMO:
+       case PRU_SLOWTIMO:
+       case PRU_PROTORCV:
+       case PRU_PROTOSEND:
+               error =  EOPNOTSUPP;
+               break;
+
+       case PRU_RCVD:
+       case PRU_RCVOOB:
+               return (EOPNOTSUPP);    /* do not free mbuf's */
+
        default:
                panic("udp_usrreq");
        }
        default:
                panic("udp_usrreq");
        }