remove ip_ctlinput, do all the work in higher level ctlinput's
[unix-history] / usr / src / sys / netinet / udp_usrreq.c
index e36e4ee..4ad6191 100644 (file)
@@ -1,23 +1,26 @@
-/*     udp_usrreq.c    4.30    82/06/20        */
-
-#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 "../net/in.h"
+/*     udp_usrreq.c    6.13    85/05/27        */
+
+#include "param.h"
+#include "dir.h"
+#include "user.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 "../net/in_pcb.h"
-#include "../net/in_systm.h"
-#include "../net/ip.h"
-#include "../net/ip_var.h"
-#include "../net/ip_icmp.h"
-#include "../net/udp.h"
-#include "../net/udp_var.h"
-#include <errno.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.
@@ -29,7 +32,7 @@ udp_init()
        udb.inp_next = udb.inp_prev = &udb;
 }
 
        udb.inp_next = udb.inp_prev = &udb;
 }
 
-int    udpcksum;
+int    udpcksum = 1;
 struct sockaddr_in udp_in = { AF_INET };
 
 udp_input(m0)
 struct sockaddr_in udp_in = { AF_INET };
 
 udp_input(m0)
@@ -38,7 +41,7 @@ udp_input(m0)
        register struct udpiphdr *ui;
        register struct inpcb *inp;
        register struct mbuf *m;
        register struct udpiphdr *ui;
        register struct inpcb *inp;
        register struct mbuf *m;
-       int len, ulen;
+       int len;
 
        /*
         * Get IP and UDP header together in first mbuf.
 
        /*
         * Get IP and UDP header together in first mbuf.
@@ -57,44 +60,39 @@ udp_input(m0)
         * 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.
         */
-       ulen = ntohs((u_short)ui->ui_ulen);
-       len = sizeof (struct udphdr) + ulen;
+       len = ntohs((u_short)ui->ui_ulen);
        if (((struct ip *)ui)->ip_len != len) {
                if (len > ((struct ip *)ui)->ip_len) {
                        udpstat.udps_badlen++;
                        goto bad;
                }
        if (((struct ip *)ui)->ip_len != len) {
                if (len > ((struct ip *)ui)->ip_len) {
                        udpstat.udps_badlen++;
                        goto bad;
                }
-               m_adj(m, ((struct ip *)ui)->ip_len - len);
+               m_adj(m, len - ((struct ip *)ui)->ip_len);
                /* (struct ip *)ui->ip_len = len; */
        }
 
        /*
         * Checksum extended UDP header and data.
         */
                /* (struct ip *)ui->ip_len = len; */
        }
 
        /*
         * 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)(sizeof (struct udphdr) + ulen));
-               if (ui->ui_sum = in_cksum(m, len)) {
+               ui->ui_len = ui->ui_ulen;
+               if (ui->ui_sum = in_cksum(m, len + sizeof (struct ip))) {
                        udpstat.udps_badsum++;
                        udpstat.udps_badsum++;
-                       printf("udp cksum %x\n", ui->ui_sum);
                        m_freem(m);
                        return;
                }
        }
 
        /*
                        m_freem(m);
                        return;
                }
        }
 
        /*
-        * Locate pcb for datagram.  On wildcard match, update
-        * control block to anchor network and host address.
+        * Locate pcb for datagram.
         */
        inp = in_pcblookup(&udb,
            ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
                INPLOOKUP_WILDCARD);
        if (inp == 0) {
         */
        inp = in_pcblookup(&udb,
            ui->ui_src, ui->ui_sport, ui->ui_dst, ui->ui_dport,
                INPLOOKUP_WILDCARD);
        if (inp == 0) {
-               struct in_addr broadcastaddr;
-
-               broadcastaddr = if_makeaddr(ui->ui_dst.s_net, INADDR_ANY);
-               if (ui->ui_dst.s_addr == broadcastaddr.s_addr)
+               /* don't send ICMP response for broadcast packet */
+               if (in_broadcast(ui->ui_dst))
                        goto bad;
                icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
                return;
                        goto bad;
                icmp_error((struct ip *)ui, ICMP_UNREACH, ICMP_UNREACH_PORT);
                return;
@@ -108,7 +106,8 @@ udp_input(m0)
        udp_in.sin_addr = ui->ui_src;
        m->m_len -= sizeof (struct udpiphdr);
        m->m_off += sizeof (struct udpiphdr);
        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)
+       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;
@@ -150,7 +149,7 @@ udp_ctlinput(cmd, arg)
 
        default:
                sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
 
        default:
                sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
-               in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
+               in_pcbnotify(&udb, sin, (int)inetctlerrmap[cmd], udp_abort);
        }
 }
 
        }
 }
 
@@ -162,6 +161,7 @@ udp_output(inp, m0)
        register struct udpiphdr *ui;
        register struct socket *so;
        register int len = 0;
        register struct udpiphdr *ui;
        register struct socket *so;
        register int len = 0;
+       register struct route *ro;
 
        /*
         * Calculate data length and get a mbuf
 
        /*
         * Calculate data length and get a mbuf
@@ -169,7 +169,7 @@ udp_output(inp, m0)
         */
        for (m = m0; m; m = m->m_next)
                len += m->m_len;
         */
        for (m = m0; m; m = m->m_next)
                len += m->m_len;
-       m = m_get(M_DONTWAIT);
+       MGET(m, M_DONTWAIT, MT_HEADER);
        if (m == 0) {
                m_freem(m0);
                return (ENOBUFS);
        if (m == 0) {
                m_freem(m0);
                return (ENOBUFS);
@@ -186,69 +186,122 @@ udp_output(inp, m0)
        ui->ui_next = ui->ui_prev = 0;
        ui->ui_x1 = 0;
        ui->ui_pr = IPPROTO_UDP;
        ui->ui_next = ui->ui_prev = 0;
        ui->ui_x1 = 0;
        ui->ui_pr = IPPROTO_UDP;
-       ui->ui_len = sizeof (struct udpiphdr) + len;
+       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 = len;
-#if vax
-       ui->ui_ulen = htons(ui->ui_ulen);
-#endif
+       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 = -1;
+       }
        ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
        ((struct ip *)ui)->ip_ttl = MAXTTL;
        so = inp->inp_socket;
        ((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));
+       if (so->so_options & SO_DONTROUTE)
+               return (ip_output(m, (struct mbuf *)0, (struct route *)0,
+                   (so->so_options & SO_BROADCAST) | IP_ROUTETOIF));
+       /*
+        * Use cached route for previous datagram if
+        * this is also to the same destination. 
+        *
+        * NB: We don't handle broadcasts because that
+        *     would require 3 subroutine calls.
+        */
+       ro = &inp->inp_route;
+#define        satosin(sa)     ((struct sockaddr_in *)(sa))
+       if (ro->ro_rt &&
+           satosin(&ro->ro_dst)->sin_addr.s_addr != ui->ui_dst.s_addr) {
+               RTFREE(ro->ro_rt);
+               ro->ro_rt = (struct rtentry *)0;
+       }
+       return (ip_output(m, (struct mbuf *)0, ro, 
+           so->so_options & SO_BROADCAST));
 }
 
 }
 
-udp_usrreq(so, req, m, addr)
+int    udp_sendspace = 2048;           /* really max datagram size */
+int    udp_recvspace = 4 * (1024+sizeof(struct sockaddr_in)); /* 4 1K dgrams */
+
+/*ARGSUSED*/
+udp_usrreq(so, req, m, nam, rights)
        struct socket *so;
        int req;
        struct socket *so;
        int req;
-       struct mbuf *m;
-       caddr_t addr;
+       struct mbuf *m, *nam, *rights;
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
 
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
 
-       if (inp == 0 && req != PRU_ATTACH)
-               return (EINVAL);
+       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;
+       }
        switch (req) {
 
        case PRU_ATTACH:
        switch (req) {
 
        case PRU_ATTACH:
-               if (inp != 0)
-                       return (EINVAL);
-               error = in_pcbattach(so, &udb, 2048, 2048,
-                               (struct sockaddr_in *)addr);
+               if (inp != NULL) {
+                       error = EINVAL;
+                       break;
+               }
+               error = in_pcballoc(so, &udb);
+               if (error)
+                       break;
+               error = soreserve(so, udp_sendspace, udp_recvspace);
+               if (error)
+                       break;
                break;
 
        case PRU_DETACH:
                break;
 
        case PRU_DETACH:
-               if (inp == 0)
-                       return (ENOTCONN);
+               if (inp == NULL) {
+                       error = ENOTCONN;
+                       break;
+               }
                in_pcbdetach(inp);
                break;
 
                in_pcbdetach(inp);
                break;
 
+       case PRU_BIND:
+               error = in_pcbbind(inp, nam);
+               break;
+
+       case PRU_LISTEN:
+               error = EOPNOTSUPP;
+               break;
+
        case PRU_CONNECT:
        case PRU_CONNECT:
-               if (inp->inp_faddr.s_addr)
-                       return (EISCONN);
-               error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
+               if (inp->inp_faddr.s_addr != INADDR_ANY) {
+                       error = EISCONN;
+                       break;
+               }
+               error = in_pcbconnect(inp, nam);
                if (error == 0)
                        soisconnected(so);
                break;
 
                if (error == 0)
                        soisconnected(so);
                break;
 
+       case PRU_CONNECT2:
+               error = EOPNOTSUPP;
+               break;
+
        case PRU_ACCEPT:
        case PRU_ACCEPT:
-               return (EOPNOTSUPP);
+               error = EOPNOTSUPP;
+               break;
 
        case PRU_DISCONNECT:
 
        case PRU_DISCONNECT:
-               if (inp->inp_faddr.s_addr == 0)
-                       return (ENOTCONN);
+               if (inp->inp_faddr.s_addr == INADDR_ANY) {
+                       error = ENOTCONN;
+                       break;
+               }
                in_pcbdisconnect(inp);
                soisdisconnected(so);
                break;
                in_pcbdisconnect(inp);
                soisdisconnected(so);
                break;
@@ -259,22 +312,35 @@ udp_usrreq(so, req, m, addr)
 
        case PRU_SEND: {
                struct in_addr laddr;
 
        case PRU_SEND: {
                struct in_addr laddr;
+               int s;
 
 
-               if (addr) {
+               if (nam) {
                        laddr = inp->inp_laddr;
                        laddr = inp->inp_laddr;
-                       if (inp->inp_faddr.s_addr)
-                               return (EISCONN);
-                       error = in_pcbconnect(inp, (struct sockaddr_in *)addr);
-                       if (error)
+                       if (inp->inp_faddr.s_addr != INADDR_ANY) {
+                               error = EISCONN;
+                               break;
+                       }
+                       /*
+                        * Must block input while temporarily connected.
+                        */
+                       s = splnet();
+                       error = in_pcbconnect(inp, nam);
+                       if (error) {
+                               splx(s);
                                break;
                                break;
+                       }
                } else {
                } else {
-                       if (inp->inp_faddr.s_addr == 0)
-                               return (ENOTCONN);
+                       if (inp->inp_faddr.s_addr == INADDR_ANY) {
+                               error = ENOTCONN;
+                               break;
+                       }
                }
                error = udp_output(inp, m);
                }
                error = udp_output(inp, m);
-               if (addr) {
+               m = NULL;
+               if (nam) {
                        in_pcbdisconnect(inp);
                        inp->inp_laddr = laddr;
                        in_pcbdisconnect(inp);
                        inp->inp_laddr = laddr;
+                       splx(s);
                }
                }
                break;
                }
                }
                break;
@@ -285,15 +351,37 @@ udp_usrreq(so, req, m, addr)
                soisdisconnected(so);
                break;
 
                soisdisconnected(so);
                break;
 
-       case PRU_CONTROL:
-               return (EOPNOTSUPP);
-
        case PRU_SOCKADDR:
        case PRU_SOCKADDR:
-               in_setsockaddr((struct sockaddr_in *)addr, inp);
+               in_setsockaddr(inp, nam);
+               break;
+
+       case PRU_PEERADDR:
+               in_setpeeraddr(inp, nam);
                break;
 
                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");
        }
+release:
+       if (m != NULL)
+               m_freem(m);
        return (error);
 }
        return (error);
 }