collect more statistics; add sanity check to avoid bad icmp packets
[unix-history] / usr / src / sys / netinet / ip_input.c
index 09c45ef..37f7c27 100644 (file)
-/* ip_input.c 1.9 81/10/29 */
+/*     ip_input.c      1.65    83/02/23        */
 
 #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/mbuf.h"
-#include "../inet/inet.h"
-#include "../inet/inet_systm.h"
-#include "../inet/imp.h"
-#include "../inet/ip.h"                        /* belongs before inet.h */
-#include "../inet/ip_icmp.h"
-#include "../inet/tcp.h"
-
-int    nosum = 0;
-
-struct ip *ip_reass();
+#include "../h/domain.h"
+#include "../h/protosw.h"
+#include "../h/socket.h"
+#include "../h/errno.h"
+#include "../h/time.h"
+#include "../h/kernel.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/tcp.h"
+
+u_char ip_protox[IPPROTO_MAX];
+int    ipqmaxlen = IFQ_MAXLEN;
+struct ifnet *ifinet;                  /* first inet interface */
 
 /*
 
 /*
- * Ip input routines.
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
  */
  */
+ip_init()
+{
+       register struct protosw *pr;
+       register int i;
+
+       pr = pffindproto(PF_INET, IPPROTO_RAW);
+       if (pr == 0)
+               panic("ip_init");
+       for (i = 0; i < IPPROTO_MAX; i++)
+               ip_protox[i] = pr - inetsw;
+       for (pr = inetdomain.dom_protosw;
+           pr <= inetdomain.dom_protoswNPROTOSW; pr++)
+               if (pr->pr_family == PF_INET &&
+                   pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
+                       ip_protox[pr->pr_protocol] = pr - inetsw;
+       ipq.next = ipq.prev = &ipq;
+       ip_id = time.tv_sec & 0xffff;
+       ipintrq.ifq_maxlen = ipqmaxlen;
+       ifinet = if_ifwithaf(AF_INET);
+}
+
+u_char ipcksum = 1;
+struct ip *ip_reass();
+struct sockaddr_in ipaddr = { AF_INET };
 
 /*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
  * try to reassamble.  If complete and fragment queue exists, discard.
  * Process options.  Pass to next level.
  */
 
 /*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
  * try to reassamble.  If complete and fragment queue exists, discard.
  * Process options.  Pass to next level.
  */
-ip_input(m0)
-       struct mbuf *m0;
+ipintr()
 {
 {
+       register struct ip *ip;
+       register struct mbuf *m;
+       struct mbuf *m0;
        register int i;
        register int i;
-       register struct ip *ip, *q;
        register struct ipq *fp;
        register struct ipq *fp;
-       register struct mbuf *m = m0;
-       int hlen;
+       int hlen, s;
 
 
-COUNT(IP_INPUT);
+next:
        /*
        /*
-        * Check header and byteswap.
+        * Get next datagram off input queue and get IP header
+        * in first mbuf.
         */
         */
+       s = splimp();
+       IF_DEQUEUE(&ipintrq, m);
+       splx(s);
+       if (m == 0)
+               return;
+       if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) &&
+           (m = m_pullup(m, sizeof (struct ip))) == 0) {
+               ipstat.ips_toosmall++;
+               goto next;
+       }
        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) {
-               printf("ip hdr ovflo\n");
-               m_freem(m);
-               return;
+               if ((m = m_pullup(m, hlen)) == 0) {
+                       ipstat.ips_badhlen++;
+                       goto next;
+               }
+               ip = mtod(m, struct ip *);
        }
        }
-       i = ip->ip_sum;
-       ip->ip_sum = 0;
-#ifdef vax
-       if (hlen == sizeof (struct ip)) {
-               asm("movl r10,r0; movl (r0)+,r1; addl2 (r0)+,r1");
-               asm("adwc (r0)+,r1; adwc (r0)+,r1; adwc (r0)+,r1");
-               asm("adwc $0,r1; ashl $-16,r1,r0; addw2 r0,r1");
-               asm("adwc $0,r1");              /* ### */
-               asm("mcoml r1,r1; movzwl r1,r1; subl2 r1,r11");
-       } else
-#endif
-               i -= cksum(m, hlen);
-       if (i) {
-               netstat.ip_badsum++;
-               if (!nosum) {
-                       m_freem(m);
-                       return;
+       if (ipcksum)
+               if (ip->ip_sum = in_cksum(m, hlen)) {
+                       ipstat.ips_badsum++;
+                       goto bad;
                }
                }
+
+       /*
+        * Convert fields to host representation.
+        */
+       ip->ip_len = ntohs((u_short)ip->ip_len);
+       if (ip->ip_len < hlen) {
+               ipstat.ips_badlen++;
+               goto bad;
        }
        }
-       ip->ip_len = ntohs(ip->ip_len);
        ip->ip_id = ntohs(ip->ip_id);
        ip->ip_id = ntohs(ip->ip_id);
-       ip->ip_off = ntohs(ip->ip_off);
+       ip->ip_off = ntohs((u_short)ip->ip_off);
 
        /*
         * Check that the amount of data in the buffers
 
        /*
         * Check that the amount of data in the buffers
@@ -72,34 +114,71 @@ COUNT(IP_INPUT);
         * 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;
-       for (; m != NULL; m = m->m_next)
+       i = -ip->ip_len;
+       m0 = m;
+       for (;;) {
                i += m->m_len;
                i += m->m_len;
-       m = m0;
-       if (i != ip->ip_len) {
-               if (i < ip->ip_len) {
-                       printf("ip_input: short packet\n");
-                       m_freem(m);
-                       return;
+               if (m->m_next == 0)
+                       break;
+               m = m->m_next;
+       }
+       if (i != 0) {
+               if (i < 0) {
+                       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, hlen);
-       if (ip->ip_dst.s_addr != n_lhost.s_addr) {
-               if (--ip->ip_ttl == 0) {
-                       icmp_error(ip, ICMP_TIMXCEED);
-                       return;
-               }
-               ip_output(dtom(ip));
-               return;
+       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;
+       }
+/* BEGIN GROT */
+#include "nd.h"
+#if NND > 0
+       /*
+        * Diskless machines don't initially know
+        * their address, so take packets from them
+        * if we're acting as a network disk server.
+        */
+       if (ip->ip_dst.s_addr == INADDR_ANY &&
+           (in_netof(ip->ip_src) == INADDR_ANY &&
+            in_lnaof(ip->ip_src) != INADDR_ANY))
+               goto ours;
+#endif
+/* END GROT */
+       ipaddr.sin_addr = ip->ip_dst;
+       if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0) {
+               ip_forward(ip);
+               goto next;
        }
 
        }
 
+ours:
        /*
         * Look for queue of fragments
         * of this datagram.
        /*
         * Look for queue of fragments
         * of this datagram.
@@ -119,9 +198,9 @@ found:
         * convert offset of this to bytes.
         */
        ip->ip_len -= hlen;
         * convert offset of this to bytes.
         */
        ip->ip_len -= hlen;
-       ip->ip_mff = 0;
+       ((struct ipasfrag *)ip)->ipf_mff = 0;
        if (ip->ip_off & IP_MF)
        if (ip->ip_off & IP_MF)
-               ip->ip_mff = 1;
+               ((struct ipasfrag *)ip)->ipf_mff = 1;
        ip->ip_off <<= 3;
 
        /*
        ip->ip_off <<= 3;
 
        /*
@@ -129,57 +208,39 @@ found:
         * or if this is not the first fragment,
         * attempt reassembly; if it succeeds, proceed.
         */
         * or if this is not the first fragment,
         * attempt reassembly; if it succeeds, proceed.
         */
-       if (ip->ip_mff || ip->ip_off) {
-               ip = ip_reass(ip, fp);
+       if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) {
+               ip = ip_reass((struct ipasfrag *)ip, fp);
                if (ip == 0)
                if (ip == 0)
-                       return;
+                       goto next;
                hlen = ip->ip_hl << 2;
                m = dtom(ip);
        } else
                if (fp)
                hlen = ip->ip_hl << 2;
                m = dtom(ip);
        } else
                if (fp)
-                       (void) ip_freef(fp);
+                       ip_freef(fp);
 
        /*
 
        /*
-        * Switch out to protocol specific routine.
-        * SHOULD GO THROUGH PROTOCOL SWITCH TABLE
+        * Switch out to protocol's input routine.
         */
         */
-       switch (ip->ip_p) {
-
-       case IPPROTO_ICMP:
-               icmp_input(m);
-               break;
-
-       case IPPROTO_TCP:
-               if (hlen > sizeof (struct ip))
-                       ip_stripoptions(ip, hlen);
-               tcp_input(m);
-               break;
-
-       case IPPROTO_UDP:
-               if (hlen > sizeof (struct ip))
-                       ip_stripoptions(ip, hlen);
-               udp_input(m);
-               break;
-
-       default:
-               raw_input(m);
-               break;
-       }
+       (*inetsw[ip_protox[ip->ip_p]].pr_input)(m);
+       goto next;
+bad:
+       m_freem(m);
+       goto next;
 }
 
 /*
  * Take incoming datagram fragment and try to
 }
 
 /*
  * Take incoming datagram fragment and try to
- * reassamble it into whole datagram.  If a chain for
+ * reassemble it into whole datagram.  If a chain for
  * reassembly of this datagram already exists, then it
  * is given as fp; otherwise have to make a chain.
  */
 struct ip *
 ip_reass(ip, fp)
  * reassembly of this datagram already exists, then it
  * is given as fp; otherwise have to make a chain.
  */
 struct ip *
 ip_reass(ip, fp)
-       register struct ip *ip;
+       register struct ipasfrag *ip;
        register struct ipq *fp;
 {
        register struct mbuf *m = dtom(ip);
        register struct ipq *fp;
 {
        register struct mbuf *m = dtom(ip);
-       register struct ip *q;
+       register struct ipasfrag *q;
        struct mbuf *t;
        int hlen = ip->ip_hl << 2;
        int i, next;
        struct mbuf *t;
        int hlen = ip->ip_hl << 2;
        int i, next;
@@ -195,23 +256,24 @@ ip_reass(ip, fp)
         * 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, MT_FTABLE)) == NULL)
                        goto dropfrag;
                        goto dropfrag;
-               t->m_off = MMINOFF;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
                fp->ipq_ttl = IPFRAGTTL;
                fp->ipq_p = ip->ip_p;
                fp->ipq_id = ip->ip_id;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
                fp->ipq_ttl = IPFRAGTTL;
                fp->ipq_p = ip->ip_p;
                fp->ipq_id = ip->ip_id;
-               fp->ipq_next = fp->ipq_prev = (struct ip *)fp;
-               fp->ipq_src = ip->ip_src;
-               fp->ipq_dst = ip->ip_dst;
+               fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
+               fp->ipq_src = ((struct ip *)ip)->ip_src;
+               fp->ipq_dst = ((struct ip *)ip)->ip_dst;
+               q = (struct ipasfrag *)fp;
+               goto insert;
        }
 
        /*
         * Find a segment which begins after this one does.
         */
        }
 
        /*
         * Find a segment which begins after this one does.
         */
-       for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next)
+       for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
                if (q->ip_off > ip->ip_off)
                        break;
 
                if (q->ip_off > ip->ip_off)
                        break;
 
@@ -220,8 +282,8 @@ ip_reass(ip, fp)
         * our data already.  If so, drop the data from the incoming
         * segment.  If it provides all of our data, drop us.
         */
         * our data already.  If so, drop the data from the incoming
         * segment.  If it provides all of our data, drop us.
         */
-       if (q->ip_prev != (struct ip *)fp) {
-               i = q->ip_prev->ip_off + q->ip_prev->ip_len - ip->ip_off;
+       if (q->ipf_prev != (struct ipasfrag *)fp) {
+               i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
                if (i > 0) {
                        if (i >= ip->ip_len)
                                goto dropfrag;
                if (i > 0) {
                        if (i >= ip->ip_len)
                                goto dropfrag;
@@ -235,30 +297,32 @@ ip_reass(ip, fp)
         * While we overlap succeeding segments trim them or,
         * if they are completely covered, dequeue them.
         */
         * While we overlap succeeding segments trim them or,
         * if they are completely covered, dequeue them.
         */
-       while (q != (struct ip *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
+       while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
                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;
                }
-               q = q->ip_next;
-               m_freem(dtom(q->ip_prev));
-               ip_deq(q->ip_prev);
+               q = q->ipf_next;
+               m_freem(dtom(q->ipf_prev));
+               ip_deq(q->ipf_prev);
        }
 
        }
 
+insert:
        /*
         * Stick new segment in its place;
         * check for complete reassembly.
         */
        /*
         * Stick new segment in its place;
         * check for complete reassembly.
         */
-       ip_enq(ip, q->ip_prev);
+       ip_enq(ip, q->ipf_prev);
        next = 0;
        next = 0;
-       for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next) {
+       for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
                if (q->ip_off != next)
                        return (0);
                next += q->ip_len;
        }
                if (q->ip_off != next)
                        return (0);
                next += q->ip_len;
        }
-       if (q->ip_prev->ip_mff)
+       if (q->ipf_prev->ipf_mff)
                return (0);
 
        /*
                return (0);
 
        /*
@@ -269,8 +333,12 @@ ip_reass(ip, fp)
        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->ip_next) != (struct ip *)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
@@ -280,14 +348,14 @@ ip_reass(ip, fp)
         */
        ip = fp->ipq_next;
        ip->ip_len = next;
         */
        ip = fp->ipq_next;
        ip->ip_len = next;
-       ip->ip_src = fp->ipq_src;
-       ip->ip_dst = fp->ipq_dst;
+       ((struct ip *)ip)->ip_src = fp->ipq_src;
+       ((struct ip *)ip)->ip_dst = fp->ipq_dst;
        remque(fp);
        remque(fp);
-       m_free(dtom(fp));
+       (void) m_free(dtom(fp));
        m = dtom(ip);
        m = dtom(ip);
-       m->m_len += sizeof (struct ip);
-       m->m_off -= sizeof (struct ip);
-       return (ip);
+       m->m_len += sizeof (struct ipasfrag);
+       m->m_off -= sizeof (struct ipasfrag);
+       return ((struct ip *)ip);
 
 dropfrag:
        m_freem(m);
 
 dropfrag:
        m_freem(m);
@@ -298,20 +366,18 @@ dropfrag:
  * Free a fragment reassembly header and all
  * associated datagrams.
  */
  * Free a fragment reassembly header and all
  * associated datagrams.
  */
-struct ipq *
 ip_freef(fp)
        struct ipq *fp;
 {
 ip_freef(fp)
        struct ipq *fp;
 {
-       register struct ip *q;
-       struct mbuf *m;
+       register struct ipasfrag *q, *p;
 
 
-       for (q = fp->ipq_next; q != (struct ip *)fp; q = q->ip_next)
+       for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
+               p = q->ipf_next;
+               ip_deq(q);
                m_freem(dtom(q));
                m_freem(dtom(q));
-       m = dtom(fp);
-       fp = fp->next;
-       remque(fp->prev);
-       m_free(m);
-       return (fp);
+       }
+       remque(fp);
+       (void) m_free(dtom(fp));
 }
 
 /*
 }
 
 /*
@@ -319,27 +385,24 @@ ip_freef(fp)
  * Like insque, but pointers in middle of structure.
  */
 ip_enq(p, prev)
  * Like insque, but pointers in middle of structure.
  */
 ip_enq(p, prev)
-       register struct ip *p;
-       register struct ip *prev;
+       register struct ipasfrag *p, *prev;
 {
 {
-COUNT(IP_ENQ);
 
 
-       p->ip_prev = prev;
-       p->ip_next = prev->ip_next;
-       prev->ip_next->ip_prev = p;
-       prev->ip_next = p;
+       p->ipf_prev = prev;
+       p->ipf_next = prev->ipf_next;
+       prev->ipf_next->ipf_prev = p;
+       prev->ipf_next = p;
 }
 
 /*
  * To ip_enq as remque is to insque.
  */
 ip_deq(p)
 }
 
 /*
  * To ip_enq as remque is to insque.
  */
 ip_deq(p)
-       register struct ip *p;
+       register struct ipasfrag *p;
 {
 {
-COUNT(IP_DEQ);
 
 
-       p->ip_prev->ip_next = p->ip_next;
-       p->ip_next->ip_prev = p->ip_prev;
+       p->ipf_prev->ipf_next = p->ipf_next;
+       p->ipf_next->ipf_prev = p->ipf_prev;
 }
 
 /*
 }
 
 /*
@@ -347,22 +410,35 @@ COUNT(IP_DEQ);
  * if a timer expires on a reassembly
  * queue, discard it.
  */
  * if a timer expires on a reassembly
  * queue, discard it.
  */
-ip_timeo()
+ip_slowtimo()
 {
 {
-       register struct ip *q;
        register struct ipq *fp;
        int s = splnet();
        register struct ipq *fp;
        int s = splnet();
-COUNT(IP_TIMEO);
 
 
-       for (fp = ipq.next; fp != &ipq; )
-               if (--fp->ipq_ttl == 0)
-                       fp = ip_freef(fp);
-               else
-                       fp = fp->next;
-       timeout(ip_timeo, 0, hz);
+       fp = ipq.next;
+       if (fp == 0) {
+               splx(s);
+               return;
+       }
+       while (fp != &ipq) {
+               --fp->ipq_ttl;
+               fp = fp->next;
+               if (fp->prev->ipq_ttl == 0)
+                       ip_freef(fp->prev);
+       }
        splx(s);
 }
 
        splx(s);
 }
 
+/*
+ * Drain off all datagram fragments.
+ */
+ip_drain()
+{
+
+       while (ipq.next != &ipq)
+               ip_freef(ipq.next);
+}
+
 /*
  * Do option processing on a datagram,
  * possibly discarding it if bad options
 /*
  * Do option processing on a datagram,
  * possibly discarding it if bad options
@@ -372,8 +448,11 @@ ip_dooptions(ip)
        struct ip *ip;
 {
        register u_char *cp;
        struct ip *ip;
 {
        register u_char *cp;
-       int opt, optlen, cnt, s;
-       struct socket *sp;
+       int opt, optlen, cnt, code, type;
+       struct in_addr *sin;
+       register struct ip_timestamp *ipt;
+       register struct ifnet *ifp;
+       struct in_addr t;
 
        cp = (u_char *)(ip + 1);
        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
 
        cp = (u_char *)(ip + 1);
        cnt = (ip->ip_hl << 2) - sizeof (struct ip);
@@ -390,110 +469,215 @@ ip_dooptions(ip)
                default:
                        break;
 
                default:
                        break;
 
+               /*
+                * Source routing with record.
+                * Find interface with current destination address.
+                * If none on this machine then drop if strictly routed,
+                * or do nothing if loosely routed.
+                * Record interface address and bring up next address
+                * component.  If strictly routed make sure next
+                * address on directly accessible net.
+                */
                case IPOPT_LSRR:
                case IPOPT_SSRR:
                case IPOPT_LSRR:
                case IPOPT_SSRR:
-                       if (cp[2] < 4 || cp[2] > optlen - 3)
+                       if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1))
+                               break;
+                       sin = (struct in_addr *)(cp + cp[2]);
+                       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;
                                break;
                                break;
-                       sp = (struct socket *)(cp+cp[2]);
-                       if (n_lhost.s_addr == *(u_long *)sp) {
-                               if (opt == IPOPT_SSRR) {
-                                       /* make sure *sp directly accessible*/
-                               }
-                               ip->ip_dst = *sp;
-                               *sp = n_lhost;
-                               cp[2] += 4;
                        }
                        }
+                       t = ip->ip_dst; ip->ip_dst = *sin; *sin = t;
+                       cp[2] += 4;
+                       if (cp[2] > optlen - (sizeof (long) - 1))
+                               break;
+                       ip->ip_dst = sin[1];
+                       if (opt == IPOPT_SSRR &&
+                           if_ifonnetof(in_netof(ip->ip_dst)) == 0)
+                               goto bad;
                        break;
 
                case IPOPT_TS:
                        break;
 
                case IPOPT_TS:
-                       if (cp[2] < 5)
+                       code = cp - (u_char *)ip;
+                       type = ICMP_PARAMPROB;
+                       ipt = (struct ip_timestamp *)cp;
+                       if (ipt->ipt_len < 5)
                                goto bad;
                                goto bad;
-                       if (cp[2] > cp[1] - 3) {
-                               if ((cp[3] & 0xf0) == 0xf0)
+                       if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
+                               if (++ipt->ipt_oflw == 0)
                                        goto bad;
                                        goto bad;
-                               cp[3] += 0x10;
                                break;
                        }
                                break;
                        }
-                       sp = (struct socket *)(cp+cp[2]);
-                       switch (cp[3] & 0xf) {
+                       sin = (struct in_addr *)(cp+cp[2]);
+                       switch (ipt->ipt_flg) {
 
                        case IPOPT_TS_TSONLY:
                                break;
 
                        case IPOPT_TS_TSANDADDR:
 
                        case IPOPT_TS_TSONLY:
                                break;
 
                        case IPOPT_TS_TSANDADDR:
-                               if (cp[2] > cp[1] - 7)
+                               if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
                                        goto bad;
+                               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 (*(u_long *)sp != n_lhost.s_addr)
-                                       break;
-                               if (cp[2] > cp[1] - 7)
+                               ipaddr.sin_addr = *sin;
+                               if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0)
+                                       continue;
+                               if (ipt->ipt_ptr + 8 > ipt->ipt_len)
                                        goto bad;
                                        goto bad;
-                               cp[1] += 4;
+                               ipt->ipt_ptr += 4;
                                break;
 
                        default:
                                goto bad;
                        }
                                break;
 
                        default:
                                goto bad;
                        }
-                       s = spl6();
-                       *(int *)sp = (time % SECDAY) * 1000 + (lbolt*1000/hz);
-                       splx(s);
-                       cp[1] += 4;
+                       *(n_time *)sin = iptime();
+                       ipt->ipt_ptr += 4;
                }
        }
        return (0);
 bad:
                }
        }
        return (0);
 bad:
-       /* SHOULD FORCE ICMP MESSAGE */
-       return (-1);
+       icmp_error(ip, type, code);
+       return (1);
 }
 
 /*
 }
 
 /*
- * Strip out IP options, e.g. before passing
- * to higher level protocol in the kernel.
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
  */
  */
-ip_stripoptions(ip)
+ip_stripoptions(ip, mopt)
        struct ip *ip;
        struct ip *ip;
+       struct mbuf *mopt;
 {
        register int i;
        register struct mbuf *m;
 {
        register int i;
        register struct mbuf *m;
-       char *op;
        int olen;
        int olen;
-COUNT(IP_OPT);
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
-       op = (caddr_t)ip + olen;
-       m = dtom(++ip);
+       m = dtom(ip);
+       ip++;
+       if (mopt) {
+               mopt->m_len = olen;
+               mopt->m_off = MMINOFF;
+               bcopy((caddr_t)ip, mtod(m, caddr_t), (unsigned)olen);
+       }
        i = m->m_len - (sizeof (struct ip) + olen);
        i = m->m_len - (sizeof (struct ip) + olen);
-       bcopy((caddr_t)ip+olen, (caddr_t)ip, i);
-       m->m_len -= i;
+       bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i);
+       m->m_len -= olen;
 }
 
 }
 
-/* stubs */
-
-icmp_error(ip, error)
+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 *in;
+       int tcp_abort(), udp_abort();
+       extern struct inpcb tcb, udb;
 
 
-       m_freem(dtom(ip));
+       if (cmd < 0 || cmd > PRC_NCMDS)
+               return;
+       if (inetctlerrmap[cmd] == 0)
+               return;         /* XXX */
+       if (cmd == PRC_IFDOWN)
+               in = &((struct sockaddr_in *)arg)->sin_addr;
+       else if (cmd == PRC_HOSTDEAD || cmd == PRC_HOSTUNREACH)
+               in = (struct in_addr *)arg;
+       else
+               in = &((struct icmp *)arg)->icmp_ip.ip_dst;
+/* THIS IS VERY QUESTIONABLE, SHOULD HIT ALL PROTOCOLS */
+       in_pcbnotify(&tcb, in, (int)inetctlerrmap[cmd], tcp_abort);
+       in_pcbnotify(&udb, in, (int)inetctlerrmap[cmd], udp_abort);
 }
 
 }
 
-icmp_input(m)
-       struct mbuf *m;
+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, MT_DATA);
+       if (mopt == NULL) {
+               m_freem(dtom(ip));
+               return;
+       }
 
 
-       printf("icmp_input %x\n", m);
-}
+       /*
+        * 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);
 
 
-udp_input(m)
-       struct mbuf *m;
-{
+       /* last 0 here means no directed broadcast */
+       if ((error = ip_output(dtom(ip), mopt, (struct route *)0, 0)) == 0) {
+               if (mcopy)
+                       m_freem(mcopy);
+               return;
+       }
+       ip = mtod(mcopy, struct ip *);
+       type = ICMP_UNREACH, code = 0;          /* need ``undefined'' */
+       switch (error) {
 
 
-       printf("udp_input %x\n", m);
-}
+       case ENETUNREACH:
+       case ENETDOWN:
+               code = ICMP_UNREACH_NET;
+               break;
 
 
-raw_input(m)
-       struct mbuf *m;
-{
+       case EMSGSIZE:
+               code = ICMP_UNREACH_NEEDFRAG;
+               break;
 
 
-       printf("raw_input %x\n", m);
+       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);
 }
 }