localize header files
[unix-history] / usr / src / sys / netinet / ip_input.c
index 17b73a7..cf9864f 100644 (file)
@@ -1,20 +1,24 @@
-/* ip_input.c 1.28 81/12/22 */
+/*     ip_input.c      1.51    82/10/09        */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
-#include "../h/clock.h"
 #include "../h/mbuf.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/mbuf.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
-#include "../net/in.h"
-#include "../net/in_systm.h"
+#include "../netinet/in.h"
+#include "../netinet/in_systm.h"
 #include "../net/if.h"
 #include "../net/if.h"
-#include "../net/ip.h"                 /* belongs before in.h */
-#include "../net/ip_var.h"
-#include "../net/ip_icmp.h"
-#include "../net/tcp.h"
+#include "../netinet/ip.h"                     /* belongs before in.h */
+#include "../netinet/ip_var.h"
+#include "../netinet/ip_icmp.h"
+#include "../netinet/tcp.h"
+#include <time.h>
+#include "../h/kernel.h"
+#include <errno.h>
 
 u_char ip_protox[IPPROTO_MAX];
 
 u_char ip_protox[IPPROTO_MAX];
+int    ipqmaxlen = IFQ_MAXLEN;
+struct ifnet *ifinet;                  /* first inet interface */
 
 /*
  * IP initialization: fill in IP protocol switch table.
 
 /*
  * IP initialization: fill in IP protocol switch table.
@@ -25,7 +29,6 @@ ip_init()
        register struct protosw *pr;
        register int i;
 
        register struct protosw *pr;
        register int i;
 
-COUNT(IP_INIT);
        pr = pffindproto(PF_INET, IPPROTO_RAW);
        if (pr == 0)
                panic("ip_init");
        pr = pffindproto(PF_INET, IPPROTO_RAW);
        if (pr == 0)
                panic("ip_init");
@@ -36,11 +39,14 @@ COUNT(IP_INIT);
                    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
                        ip_protox[pr->pr_protocol] = pr - protosw;
        ipq.next = ipq.prev = &ipq;
                    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
                        ip_protox[pr->pr_protocol] = pr - protosw;
        ipq.next = ipq.prev = &ipq;
-       ip_id = time & 0xffff;
+       ip_id = time.tv_sec & 0xffff;
+       ipintrq.ifq_maxlen = ipqmaxlen;
+       ifinet = if_ifwithaf(AF_INET);
 }
 
 u_char ipcksum = 1;
 struct ip *ip_reass();
 }
 
 u_char ipcksum = 1;
 struct ip *ip_reass();
+struct sockaddr_in ipaddr = { AF_INET };
 
 /*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
 
 /*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
@@ -56,7 +62,6 @@ ipintr()
        register struct ipq *fp;
        int hlen, s;
 
        register struct ipq *fp;
        int hlen, s;
 
-COUNT(IPINTR);
 next:
        /*
         * Get next datagram off input queue and get IP header
 next:
        /*
         * Get next datagram off input queue and get IP header
@@ -67,13 +72,13 @@ next:
        splx(s);
        if (m == 0)
                return;
        splx(s);
        if (m == 0)
                return;
-       if (m->m_len < sizeof (struct ip) &&
-           m_pullup(m, sizeof (struct ip)) == 0)
-               goto bad;
+       if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) &&
+           (m = m_pullup(m, sizeof (struct ip))) == 0)
+               return;
        ip = mtod(m, struct ip *);
        if ((hlen = ip->ip_hl << 2) > m->m_len) {
        ip = mtod(m, struct ip *);
        if ((hlen = ip->ip_hl << 2) > m->m_len) {
-               if (m_pullup(m, hlen) == 0)
-                       goto bad;
+               if ((m = m_pullup(m, hlen)) == 0)
+                       return;
                ip = mtod(m, struct ip *);
        }
        if (ipcksum)
                ip = mtod(m, struct ip *);
        }
        if (ipcksum)
@@ -98,40 +103,57 @@ next:
         * Trim mbufs if longer than we expect.
         * Drop packet if shorter than we expect.
         */
         * Trim mbufs if longer than we expect.
         * Drop packet if shorter than we expect.
         */
-       i = 0;
+       i = -ip->ip_len;
        m0 = m;
        m0 = m;
-       for (; m != NULL; m = m->m_next)
+       for (;;) {
                i += m->m_len;
                i += m->m_len;
-       m = m0;
-       if (i != ip->ip_len) {
-               if (i < ip->ip_len) {
+               if (m->m_next == 0)
+                       break;
+               m = m->m_next;
+       }
+       if (i != 0) {
+               if (i < 0) {
                        ipstat.ips_tooshort++;
                        goto bad;
                }
                        ipstat.ips_tooshort++;
                        goto bad;
                }
-               m_adj(m, ip->ip_len - i);
+               if (i <= m->m_len)
+                       m->m_len -= i;
+               else
+                       m_adj(m0, -i);
        }
        }
+       m = m0;
 
        /*
         * Process options and, if not destined for us,
 
        /*
         * Process options and, if not destined for us,
-        * ship it on.
+        * ship it on.  ip_dooptions returns 1 when an
+        * error was detected (causing an icmp message
+        * to be sent).
         */
         */
-       if (hlen > sizeof (struct ip))
-               ip_dooptions(ip);
-       if (ifnet && ip->ip_dst.s_addr != ifnet->if_addr.s_addr &&
-           if_ifwithaddr(ip->ip_dst) == 0) {
-printf("ip->ip_dst %x ip->ip_ttl %x\n", ip->ip_dst, ip->ip_ttl);
-               if (--ip->ip_ttl == 0) {
-                       icmp_error(ip, ICMP_TIMXCEED, 0);
-                       goto next;
-               }
-               mopt = m_get(M_DONTWAIT);
-               if (mopt == 0)
-                       goto bad;
-               ip_stripoptions(ip, mopt);
-               (void) ip_output(m0, mopt);
+       if (hlen > sizeof (struct ip) && ip_dooptions(ip))
+               goto next;
+
+       /*
+        * Fast check on the first internet
+        * interface in the list.
+        */
+       if (ifinet) {
+               struct sockaddr_in *sin;
+
+               sin = (struct sockaddr_in *)&ifinet->if_addr;
+               if (sin->sin_addr.s_addr == ip->ip_dst.s_addr)
+                       goto ours;
+               sin = (struct sockaddr_in *)&ifinet->if_broadaddr;
+               if ((ifinet->if_flags & IFF_BROADCAST) &&
+                   sin->sin_addr.s_addr == ip->ip_dst.s_addr)
+                       goto ours;
+       }
+       ipaddr.sin_addr = ip->ip_dst;
+       if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0) {
+               ip_forward(ip);
                goto next;
        }
 
                goto next;
        }
 
+ours:
        /*
         * Look for queue of fragments
         * of this datagram.
        /*
         * Look for queue of fragments
         * of this datagram.
@@ -197,7 +219,6 @@ ip_reass(ip, fp)
        struct mbuf *t;
        int hlen = ip->ip_hl << 2;
        int i, next;
        struct mbuf *t;
        int hlen = ip->ip_hl << 2;
        int i, next;
-COUNT(IP_REASS);
 
        /*
         * Presence of header sizes in mbufs
 
        /*
         * Presence of header sizes in mbufs
@@ -210,9 +231,8 @@ COUNT(IP_REASS);
         * If first fragment to arrive, create a reassembly queue.
         */
        if (fp == 0) {
         * If first fragment to arrive, create a reassembly queue.
         */
        if (fp == 0) {
-               if ((t = m_get(1)) == NULL)
+               if ((t = m_get(M_WAIT)) == NULL)
                        goto dropfrag;
                        goto dropfrag;
-               t->m_off = MMINOFF;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
                fp->ipq_ttl = IPFRAGTTL;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
                fp->ipq_ttl = IPFRAGTTL;
@@ -256,6 +276,7 @@ COUNT(IP_REASS);
                i = (ip->ip_off + ip->ip_len) - q->ip_off;
                if (i < q->ip_len) {
                        q->ip_len -= i;
                i = (ip->ip_off + ip->ip_len) - q->ip_off;
                if (i < q->ip_len) {
                        q->ip_len -= i;
+                       q->ip_off += i;
                        m_adj(dtom(q), i);
                        break;
                }
                        m_adj(dtom(q), i);
                        break;
                }
@@ -287,8 +308,12 @@ insert:
        t = m->m_next;
        m->m_next = 0;
        m_cat(m, t);
        t = m->m_next;
        m->m_next = 0;
        m_cat(m, t);
-       while ((q = q->ipf_next) != (struct ipasfrag *)fp)
-               m_cat(m, dtom(q));
+       q = q->ipf_next;
+       while (q != (struct ipasfrag *)fp) {
+               t = dtom(q);
+               q = q->ipf_next;
+               m_cat(m, t);
+       }
 
        /*
         * Create header for new ip packet by
 
        /*
         * Create header for new ip packet by
@@ -322,7 +347,6 @@ ip_freef(fp)
 {
        register struct ipasfrag *q;
        struct mbuf *m;
 {
        register struct ipasfrag *q;
        struct mbuf *m;
-COUNT(IP_FREEF);
 
        for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
                m_freem(dtom(q));
 
        for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
                m_freem(dtom(q));
@@ -341,7 +365,6 @@ ip_enq(p, prev)
        register struct ipasfrag *p, *prev;
 {
 
        register struct ipasfrag *p, *prev;
 {
 
-COUNT(IP_ENQ);
        p->ipf_prev = prev;
        p->ipf_next = prev->ipf_next;
        prev->ipf_next->ipf_prev = p;
        p->ipf_prev = prev;
        p->ipf_next = prev->ipf_next;
        prev->ipf_next->ipf_prev = p;
@@ -355,7 +378,6 @@ ip_deq(p)
        register struct ipasfrag *p;
 {
 
        register struct ipasfrag *p;
 {
 
-COUNT(IP_DEQ);
        p->ipf_prev->ipf_next = p->ipf_next;
        p->ipf_next->ipf_prev = p->ipf_prev;
 }
        p->ipf_prev->ipf_next = p->ipf_next;
        p->ipf_next->ipf_prev = p->ipf_prev;
 }
@@ -370,7 +392,6 @@ ip_slowtimo()
        register struct ipq *fp;
        int s = splnet();
 
        register struct ipq *fp;
        int s = splnet();
 
-COUNT(IP_SLOWTIMO);
        fp = ipq.next;
        if (fp == 0) {
                splx(s);
        fp = ipq.next;
        if (fp == 0) {
                splx(s);
@@ -390,7 +411,6 @@ COUNT(IP_SLOWTIMO);
 ip_drain()
 {
 
 ip_drain()
 {
 
-COUNT(IP_DRAIN);
        while (ipq.next != &ipq)
                (void) ip_freef(ipq.next);
 }
        while (ipq.next != &ipq)
                (void) ip_freef(ipq.next);
 }
@@ -404,13 +424,12 @@ ip_dooptions(ip)
        struct ip *ip;
 {
        register u_char *cp;
        struct ip *ip;
 {
        register u_char *cp;
-       int opt, optlen, cnt;
+       int opt, optlen, cnt, code, type;
        struct in_addr *sin;
        register struct ip_timestamp *ipt;
        register struct ifnet *ifp;
        struct in_addr t;
 
        struct in_addr *sin;
        register struct ip_timestamp *ipt;
        register struct ifnet *ifp;
        struct in_addr t;
 
-COUNT(IP_DOOPTIONS);
        cp = (u_char *)(ip + 1);
        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
        for (; cnt > 0; cnt -= optlen, cp += optlen) {
        cp = (u_char *)(ip + 1);
        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
        for (; cnt > 0; cnt -= optlen, cp += optlen) {
@@ -436,10 +455,13 @@ COUNT(IP_DOOPTIONS);
                 * address on directly accessible net.
                 */
                case IPOPT_LSRR:
                 * address on directly accessible net.
                 */
                case IPOPT_LSRR:
+               case IPOPT_SSRR:
                        if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1))
                                break;
                        sin = (struct in_addr *)(cp + cp[2]);
                        if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1))
                                break;
                        sin = (struct in_addr *)(cp + cp[2]);
-                       ifp = if_ifwithaddr(*sin);
+                       ipaddr.sin_addr = *sin;
+                       ifp = if_ifwithaddr((struct sockaddr *)&ipaddr);
+                       type = ICMP_UNREACH, code = ICMP_UNREACH_SRCFAIL;
                        if (ifp == 0) {
                                if (opt == IPOPT_SSRR)
                                        goto bad;
                        if (ifp == 0) {
                                if (opt == IPOPT_SSRR)
                                        goto bad;
@@ -450,11 +472,14 @@ COUNT(IP_DOOPTIONS);
                        if (cp[2] > optlen - (sizeof (long) - 1))
                                break;
                        ip->ip_dst = sin[1];
                        if (cp[2] > optlen - (sizeof (long) - 1))
                                break;
                        ip->ip_dst = sin[1];
-                       if (opt == IPOPT_SSRR && if_ifonnetof(ip->ip_dst)==0)
+                       if (opt == IPOPT_SSRR &&
+                           if_ifonnetof(in_netof(ip->ip_dst)) == 0)
                                goto bad;
                        break;
 
                case IPOPT_TS:
                                goto bad;
                        break;
 
                case IPOPT_TS:
+                       code = cp - (u_char *)ip;
+                       type = ICMP_PARAMPROB;
                        ipt = (struct ip_timestamp *)cp;
                        if (ipt->ipt_len < 5)
                                goto bad;
                        ipt = (struct ip_timestamp *)cp;
                        if (ipt->ipt_len < 5)
                                goto bad;
@@ -472,12 +497,14 @@ COUNT(IP_DOOPTIONS);
                        case IPOPT_TS_TSANDADDR:
                                if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
                        case IPOPT_TS_TSANDADDR:
                                if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
-                               /* stamp with ``first'' interface address */
-                               *sin++ = ifnet->if_addr;
+                               if (ifinet == 0)
+                                       goto bad;       /* ??? */
+                               *sin++ = ((struct sockaddr_in *)&ifinet->if_addr)->sin_addr;
                                break;
 
                        case IPOPT_TS_PRESPEC:
                                break;
 
                        case IPOPT_TS_PRESPEC:
-                               if (if_ifwithaddr(*sin) == 0)
+                               ipaddr.sin_addr = *sin;
+                               if (!if_ifwithaddr((struct sockaddr *)&ipaddr))
                                        continue;
                                if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
                                        continue;
                                if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
@@ -491,10 +518,10 @@ COUNT(IP_DOOPTIONS);
                        ipt->ipt_ptr += 4;
                }
        }
                        ipt->ipt_ptr += 4;
                }
        }
-       return;
+       return (0);
 bad:
 bad:
-       /* SHOULD FORCE ICMP MESSAGE */
-       return;
+       icmp_error(ip, type, code);
+       return (1);
 }
 
 /*
 }
 
 /*
@@ -510,7 +537,6 @@ ip_stripoptions(ip, mopt)
        register int i;
        register struct mbuf *m;
        int olen;
        register int i;
        register struct mbuf *m;
        int olen;
-COUNT(IP_STRIPOPTIONS);
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
        m = dtom(ip);
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
        m = dtom(ip);
@@ -524,3 +550,109 @@ COUNT(IP_STRIPOPTIONS);
        bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i);
        m->m_len -= olen;
 }
        bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i);
        m->m_len -= olen;
 }
+
+u_char inetctlerrmap[] = {
+       ECONNABORTED,   ECONNABORTED,   0,              0,
+       0,              0,
+       EHOSTDOWN,      EHOSTUNREACH,   ENETUNREACH,    EHOSTUNREACH,
+       ECONNREFUSED,   ECONNREFUSED,   EMSGSIZE,       0,
+       0,              0,              0,              0
+};
+
+ip_ctlinput(cmd, arg)
+       int cmd;
+       caddr_t arg;
+{
+       struct in_addr *sin;
+       int tcp_abort(), udp_abort();
+       extern struct inpcb tcb, udb;
+
+       if (cmd < 0 || cmd > PRC_NCMDS)
+               return;
+       if (inetctlerrmap[cmd] == 0)
+               return;         /* XXX */
+       if (cmd == PRC_IFDOWN)
+               sin = &((struct sockaddr_in *)arg)->sin_addr;
+       else if (cmd == PRC_HOSTDEAD || cmd == PRC_HOSTUNREACH)
+               sin = (struct in_addr *)arg;
+       else
+               sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
+       in_pcbnotify(&tcb, sin, inetctlerrmap[cmd], tcp_abort);
+       in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
+}
+
+int    ipprintfs = 0;
+int    ipforwarding = 1;
+/*
+ * Forward a packet.  If some error occurs return the sender
+ * and icmp packet.  Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repetoire
+ * of codes and types.
+ */
+ip_forward(ip)
+       register struct ip *ip;
+{
+       register int error, type, code;
+       struct mbuf *mopt, *mcopy;
+
+       if (ipprintfs)
+               printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
+                       ip->ip_dst, ip->ip_ttl);
+       if (ipforwarding == 0) {
+               /* can't tell difference between net and host */
+               type = ICMP_UNREACH, code = ICMP_UNREACH_NET;
+               goto sendicmp;
+       }
+       if (ip->ip_ttl < IPTTLDEC) {
+               type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
+               goto sendicmp;
+       }
+       ip->ip_ttl -= IPTTLDEC;
+       mopt = m_get(M_DONTWAIT);
+       if (mopt == 0) {
+               m_freem(dtom(ip));
+               return;
+       }
+
+       /*
+        * Save at most 64 bytes of the packet in case
+        * we need to generate an ICMP message to the src.
+        */
+       mcopy = m_copy(dtom(ip), 0, imin(ip->ip_len, 64));
+       ip_stripoptions(ip, mopt);
+
+       /* last 0 here means no directed broadcast */
+       if ((error = ip_output(dtom(ip), mopt, 0, 0)) == 0) {
+               if (mcopy)
+                       m_freem(mcopy);
+               return;
+       }
+       ip = mtod(mcopy, struct ip *);
+       type = ICMP_UNREACH, code = 0;          /* need ``undefined'' */
+       switch (error) {
+
+       case ENETUNREACH:
+       case ENETDOWN:
+               code = ICMP_UNREACH_NET;
+               break;
+
+       case EMSGSIZE:
+               code = ICMP_UNREACH_NEEDFRAG;
+               break;
+
+       case EPERM:
+               code = ICMP_UNREACH_PORT;
+               break;
+
+       case ENOBUFS:
+               type = ICMP_SOURCEQUENCH;
+               break;
+
+       case EHOSTDOWN:
+       case EHOSTUNREACH:
+               code = ICMP_UNREACH_HOST;
+               break;
+       }
+sendicmp:
+       icmp_error(ip, type, code);
+}