Van's suggestion: add a means to let kernel set IP header ident #
[unix-history] / usr / src / sys / netinet / ip_input.c
index 8c45890..1092270 100644 (file)
@@ -1,36 +1,71 @@
 /*
 /*
- * Copyright (c) 1982 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1982, 1986, 1988, 1993 Regents of the University of California.
+ * All rights reserved.
  *
  *
- *     @(#)ip_input.c  6.18 (Berkeley) %G%
+ * %sccs.include.redist.c%
+ *
+ *     @(#)ip_input.c  7.26 (Berkeley) %G%
  */
 
  */
 
-#include "param.h"
-#include "systm.h"
-#include "mbuf.h"
-#include "domain.h"
-#include "protosw.h"
-#include "socket.h"
-#include "errno.h"
-#include "time.h"
-#include "kernel.h"
-
-#include "../net/if.h"
-#include "../net/route.h"
-
-#include "in.h"
-#include "in_pcb.h"
-#include "in_systm.h"
-#include "in_var.h"
-#include "ip.h"
-#include "ip_var.h"
-#include "ip_icmp.h"
-#include "tcp.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.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/in_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+
+#ifndef        IPFORWARDING
+#ifdef GATEWAY
+#define        IPFORWARDING    1
+#else /* GATEWAY */
+#define        IPFORWARDING    0
+#endif /* GATEWAY */
+#endif /* IPFORWARDING */
+#ifndef        IPSENDREDIRECTS
+#define        IPSENDREDIRECTS 1
+#endif
+int    ipprintfs = 0;
+int    ipforwarding = IPFORWARDING;
+extern int in_interfaces;
+int    ipsendredirects = IPSENDREDIRECTS;
+
+#ifndef        IPFORWARDING
+#ifdef GATEWAY
+#define        IPFORWARDING    1       /* forward IP packets not for us */
+#else /* GATEWAY */
+#define        IPFORWARDING    0       /* don't forward IP packets not for us */
+#endif /* GATEWAY */
+#endif /* IPFORWARDING */
+#ifndef        IPSENDREDIRECTS
+#define        IPSENDREDIRECTS 1
+#endif
+int    ipforwarding = IPFORWARDING;
+int    ipsendredirects = IPSENDREDIRECTS;
+#ifdef DIAGNOSTIC
+int    ipprintfs = 0;
+#endif
 
 
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
 u_char ip_protox[IPPROTO_MAX];
 int    ipqmaxlen = IFQ_MAXLEN;
 struct in_ifaddr *in_ifaddr;                   /* first inet address */
 u_char ip_protox[IPPROTO_MAX];
 int    ipqmaxlen = IFQ_MAXLEN;
 struct in_ifaddr *in_ifaddr;                   /* first inet address */
+struct ifqueue ipintrq;
 
 /*
  * We need to save the IP options in case a protocol wants to respond
 
 /*
  * We need to save the IP options in case a protocol wants to respond
@@ -41,11 +76,18 @@ struct      in_ifaddr *in_ifaddr;                   /* first inet address */
  */
 int    ip_nhops = 0;
 static struct ip_srcrt {
  */
 int    ip_nhops = 0;
 static struct ip_srcrt {
+       struct  in_addr dst;                    /* final destination */
+       struct  in_addr dst;                    /* final destination */
        char    nop;                            /* one NOP to align */
        char    srcopt[IPOPT_OFFSET + 1];       /* OPTVAL, OLEN and OFFSET */
        char    nop;                            /* one NOP to align */
        char    srcopt[IPOPT_OFFSET + 1];       /* OPTVAL, OLEN and OFFSET */
-       struct  in_addr route[MAX_IPOPTLEN];
+       struct  in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
 } ip_srcrt;
 
 } ip_srcrt;
 
+#ifdef GATEWAY
+extern int if_index;
+u_long *ip_ifmatrix;
+#endif
+
 /*
  * IP initialization: fill in IP protocol switch table.
  * All protocols not implemented in kernel go to raw IP protocol handler.
 /*
  * IP initialization: fill in IP protocol switch table.
  * All protocols not implemented in kernel go to raw IP protocol handler.
@@ -68,27 +110,27 @@ ip_init()
        ipq.next = ipq.prev = &ipq;
        ip_id = time.tv_sec & 0xffff;
        ipintrq.ifq_maxlen = ipqmaxlen;
        ipq.next = ipq.prev = &ipq;
        ip_id = time.tv_sec & 0xffff;
        ipintrq.ifq_maxlen = ipqmaxlen;
+#ifdef GATEWAY
+       i = (if_index + 1) * (if_index + 1) * sizeof (u_long);
+       ip_ifmatrix = (u_long *) malloc(i, M_RTABLE, M_WAITOK);
+       bzero((char *)ip_ifmatrix, i);
+#endif
 }
 
 }
 
-u_char ipcksum = 1;
 struct ip *ip_reass();
 struct ip *ip_reass();
-struct sockaddr_in ipaddr = { AF_INET };
+struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
 struct route ipforward_rt;
 
 /*
  * Ip input routine.  Checksum and byte swap header.  If fragmented
 struct route ipforward_rt;
 
 /*
  * 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.
+ * try to reassemble.  Process options.  Pass to next level.
  */
 ipintr()
 {
        register struct ip *ip;
        register struct mbuf *m;
  */
 ipintr()
 {
        register struct ip *ip;
        register struct mbuf *m;
-       struct mbuf *m0;
-       register int i;
        register struct ipq *fp;
        register struct in_ifaddr *ia;
        register struct ipq *fp;
        register struct in_ifaddr *ia;
-       struct ifnet *ifp;
        int hlen, s;
 
 next:
        int hlen, s;
 
 next:
@@ -97,10 +139,14 @@ next:
         * in first mbuf.
         */
        s = splimp();
         * in first mbuf.
         */
        s = splimp();
-       IF_DEQUEUEIF(&ipintrq, m, ifp);
+       IF_DEQUEUE(&ipintrq, m);
        splx(s);
        if (m == 0)
                return;
        splx(s);
        if (m == 0)
                return;
+#ifdef DIAGNOSTIC
+       if ((m->m_flags & M_PKTHDR) == 0)
+               panic("ipintr no HDR");
+#endif
        /*
         * If no IP addresses have been set yet but the interfaces
         * are receiving, can't do anything with incoming packets yet.
        /*
         * If no IP addresses have been set yet but the interfaces
         * are receiving, can't do anything with incoming packets yet.
@@ -108,12 +154,16 @@ next:
        if (in_ifaddr == NULL)
                goto bad;
        ipstat.ips_total++;
        if (in_ifaddr == NULL)
                goto bad;
        ipstat.ips_total++;
-       if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) &&
+       if (m->m_len < sizeof (struct ip) &&
            (m = m_pullup(m, sizeof (struct ip))) == 0) {
                ipstat.ips_toosmall++;
                goto next;
        }
        ip = mtod(m, struct ip *);
            (m = m_pullup(m, sizeof (struct ip))) == 0) {
                ipstat.ips_toosmall++;
                goto next;
        }
        ip = mtod(m, struct ip *);
+       if (ip->ip_v != IPVERSION) {
+               ipstat.ips_badvers++;
+               goto bad;
+       }
        hlen = ip->ip_hl << 2;
        if (hlen < sizeof(struct ip)) { /* minimum header length */
                ipstat.ips_badhlen++;
        hlen = ip->ip_hl << 2;
        if (hlen < sizeof(struct ip)) { /* minimum header length */
                ipstat.ips_badhlen++;
@@ -126,22 +176,21 @@ next:
                }
                ip = mtod(m, struct ip *);
        }
                }
                ip = mtod(m, struct ip *);
        }
-       if (ipcksum)
-               if (ip->ip_sum = in_cksum(m, hlen)) {
-                       ipstat.ips_badsum++;
-                       goto bad;
-               }
+       if (ip->ip_sum = in_cksum(m, hlen)) {
+               ipstat.ips_badsum++;
+               goto bad;
+       }
 
        /*
         * Convert fields to host representation.
         */
 
        /*
         * Convert fields to host representation.
         */
-       ip->ip_len = ntohs((u_short)ip->ip_len);
+       NTOHS(ip->ip_len);
        if (ip->ip_len < hlen) {
                ipstat.ips_badlen++;
                goto bad;
        }
        if (ip->ip_len < hlen) {
                ipstat.ips_badlen++;
                goto bad;
        }
-       ip->ip_id = ntohs(ip->ip_id);
-       ip->ip_off = ntohs((u_short)ip->ip_off);
+       NTOHS(ip->ip_id);
+       NTOHS(ip->ip_off);
 
        /*
         * Check that the amount of data in the buffers
 
        /*
         * Check that the amount of data in the buffers
@@ -149,26 +198,17 @@ 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 = -(u_short)ip->ip_len;
-       m0 = m;
-       for (;;) {
-               i += m->m_len;
-               if (m->m_next == 0)
-                       break;
-               m = m->m_next;
+       if (m->m_pkthdr.len < ip->ip_len) {
+               ipstat.ips_tooshort++;
+               goto bad;
        }
        }
-       if (i != 0) {
-               if (i < 0) {
-                       ipstat.ips_tooshort++;
-                       m = m0;
-                       goto bad;
-               }
-               if (i <= m->m_len)
-                       m->m_len -= i;
-               else
-                       m_adj(m0, -i);
+       if (m->m_pkthdr.len > ip->ip_len) {
+               if (m->m_len == m->m_pkthdr.len) {
+                       m->m_len = ip->ip_len;
+                       m->m_pkthdr.len = ip->ip_len;
+               } else
+                       m_adj(m, ip->ip_len - m->m_pkthdr.len);
        }
        }
-       m = m0;
 
        /*
         * Process options and, if not destined for us,
 
        /*
         * Process options and, if not destined for us,
@@ -177,7 +217,7 @@ next:
         * to be sent and the original packet to be freed).
         */
        ip_nhops = 0;           /* for source routed packets */
         * to be sent and the original packet to be freed).
         */
        ip_nhops = 0;           /* for source routed packets */
-       if (hlen > sizeof (struct ip) && ip_dooptions(ip, ifp))
+       if (hlen > sizeof (struct ip) && ip_dooptions(m))
                goto next;
 
        /*
                goto next;
 
        /*
@@ -190,7 +230,7 @@ next:
                        goto ours;
                if (
 #ifdef DIRECTED_BROADCAST
                        goto ours;
                if (
 #ifdef DIRECTED_BROADCAST
-                   ia->ia_ifp == ifp &&
+                   ia->ia_ifp == m->m_pkthdr.rcvif &&
 #endif
                    (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
                        u_long t;
 #endif
                    (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
                        u_long t;
@@ -211,6 +251,54 @@ next:
                                goto ours;
                }
        }
                                goto ours;
                }
        }
+       if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+               struct in_multi *inm;
+#ifdef MROUTING
+               extern struct socket *ip_mrouter;
+
+               if (ip_mrouter) {
+                       /*
+                        * If we are acting as a multicast router, all
+                        * incoming multicast packets are passed to the
+                        * kernel-level multicast forwarding function.
+                        * The packet is returned (relatively) intact; if
+                        * ip_mforward() returns a non-zero value, the packet
+                        * must be discarded, else it may be accepted below.
+                        *
+                        * (The IP ident field is put in the same byte order
+                        * as expected when ip_mforward() is called from
+                        * ip_output().)
+                        */
+                       ip->ip_id = htons(ip->ip_id);
+                       if (ip_mforward(m, m->m_pkthdr.rcvif) != 0) {
+                               ipstat.ips_cantforward++;
+                               m_freem(m);
+                               goto next;
+                       }
+                       ip->ip_id = ntohs(ip->ip_id);
+
+                       /*
+                        * The process-level routing demon needs to receive
+                        * all multicast IGMP packets, whether or not this
+                        * host belongs to their destination groups.
+                        */
+                       if (ip->ip_p == IPPROTO_IGMP)
+                               goto ours;
+                       ipstat.ips_forward++;
+               }
+#endif
+               /*
+                * See if we belong to the destination multicast group on the
+                * arrival interface.
+                */
+               IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
+               if (inm == NULL) {
+                       ipstat.ips_cantforward++;
+                       m_freem(m);
+                       goto next;
+               }
+               goto ours;
+       }
        if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
                goto ours;
        if (ip->ip_dst.s_addr == INADDR_ANY)
        if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
                goto ours;
        if (ip->ip_dst.s_addr == INADDR_ANY)
@@ -219,53 +307,76 @@ next:
        /*
         * Not for us; forward if possible and desirable.
         */
        /*
         * Not for us; forward if possible and desirable.
         */
-       ip_forward(ip, ifp);
+       if (ipforwarding == 0) {
+               ipstat.ips_cantforward++;
+               m_freem(m);
+       } else
+               ip_forward(m, 0);
        goto next;
 
 ours:
        /*
        goto next;
 
 ours:
        /*
-        * Look for queue of fragments
-        * of this datagram.
+        * If offset or IP_MF are set, must reassemble.
+        * Otherwise, nothing need be done.
+        * (We could look in the reassembly queue to see
+        * if the packet was previously fragmented,
+        * but it's not worth the time; just let them time out.)
         */
         */
-       for (fp = ipq.next; fp != &ipq; fp = fp->next)
-               if (ip->ip_id == fp->ipq_id &&
-                   ip->ip_src.s_addr == fp->ipq_src.s_addr &&
-                   ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
-                   ip->ip_p == fp->ipq_p)
-                       goto found;
-       fp = 0;
+       if (ip->ip_off &~ IP_DF) {
+               if (m->m_flags & M_EXT) {               /* XXX */
+                       if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
+                               ipstat.ips_toosmall++;
+                               goto next;
+                       }
+                       ip = mtod(m, struct ip *);
+               }
+               /*
+                * Look for queue of fragments
+                * of this datagram.
+                */
+               for (fp = ipq.next; fp != &ipq; fp = fp->next)
+                       if (ip->ip_id == fp->ipq_id &&
+                           ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+                           ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+                           ip->ip_p == fp->ipq_p)
+                               goto found;
+               fp = 0;
 found:
 
 found:
 
-       /*
-        * Adjust ip_len to not reflect header,
-        * set ip_mff if more fragments are expected,
-        * convert offset of this to bytes.
-        */
-       ip->ip_len -= hlen;
-       ((struct ipasfrag *)ip)->ipf_mff = 0;
-       if (ip->ip_off & IP_MF)
-               ((struct ipasfrag *)ip)->ipf_mff = 1;
-       ip->ip_off <<= 3;
+               /*
+                * Adjust ip_len to not reflect header,
+                * set ip_mff if more fragments are expected,
+                * convert offset of this to bytes.
+                */
+               ip->ip_len -= hlen;
+               ((struct ipasfrag *)ip)->ipf_mff &= ~1;
+               if (ip->ip_off & IP_MF)
+                       ((struct ipasfrag *)ip)->ipf_mff |= 1;
+               ip->ip_off <<= 3;
 
 
-       /*
-        * If datagram marked as having more fragments
-        * or if this is not the first fragment,
-        * attempt reassembly; if it succeeds, proceed.
-        */
-       if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) {
-               ipstat.ips_fragments++;
-               ip = ip_reass((struct ipasfrag *)ip, fp);
-               if (ip == 0)
-                       goto next;
-               m = dtom(ip);
+               /*
+                * If datagram marked as having more fragments
+                * or if this is not the first fragment,
+                * attempt reassembly; if it succeeds, proceed.
+                */
+               if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
+                       ipstat.ips_fragments++;
+                       ip = ip_reass((struct ipasfrag *)ip, fp);
+                       if (ip == 0)
+                               goto next;
+                       ipstat.ips_reassembled++;
+                       m = dtom(ip);
+               } else
+                       if (fp)
+                               ip_freef(fp);
        } else
        } else
-               if (fp)
-                       ip_freef(fp);
+               ip->ip_len -= hlen;
 
        /*
         * Switch out to protocol's input routine.
         */
 
        /*
         * Switch out to protocol's input routine.
         */
-       (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, ifp);
+       ipstat.ips_delivered++;
+       (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
        goto next;
 bad:
        m_freem(m);
        goto next;
 bad:
        m_freem(m);
@@ -293,14 +404,14 @@ ip_reass(ip, fp)
         * Presence of header sizes in mbufs
         * would confuse code below.
         */
         * Presence of header sizes in mbufs
         * would confuse code below.
         */
-       m->m_off += hlen;
+       m->m_data += hlen;
        m->m_len -= hlen;
 
        /*
         * If first fragment to arrive, create a reassembly queue.
         */
        if (fp == 0) {
        m->m_len -= hlen;
 
        /*
         * If first fragment to arrive, create a reassembly queue.
         */
        if (fp == 0) {
-               if ((t = m_get(M_WAIT, MT_FTABLE)) == NULL)
+               if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
                        goto dropfrag;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
                        goto dropfrag;
                fp = mtod(t, struct ipq *);
                insque(fp, &ipq);
@@ -366,7 +477,7 @@ insert:
                        return (0);
                next += q->ip_len;
        }
                        return (0);
                next += q->ip_len;
        }
-       if (q->ipf_prev->ipf_mff)
+       if (q->ipf_prev->ipf_mff & 1)
                return (0);
 
        /*
                return (0);
 
        /*
@@ -392,13 +503,21 @@ insert:
         */
        ip = fp->ipq_next;
        ip->ip_len = next;
         */
        ip = fp->ipq_next;
        ip->ip_len = next;
+       ip->ipf_mff &= ~1;
        ((struct ip *)ip)->ip_src = fp->ipq_src;
        ((struct ip *)ip)->ip_dst = fp->ipq_dst;
        remque(fp);
        (void) m_free(dtom(fp));
        m = dtom(ip);
        m->m_len += (ip->ip_hl << 2);
        ((struct ip *)ip)->ip_src = fp->ipq_src;
        ((struct ip *)ip)->ip_dst = fp->ipq_dst;
        remque(fp);
        (void) m_free(dtom(fp));
        m = dtom(ip);
        m->m_len += (ip->ip_hl << 2);
-       m->m_off -= (ip->ip_hl << 2);
+       m->m_data -= (ip->ip_hl << 2);
+       /* some debugging cruft by sklower, below, will go away soon */
+       if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
+               register int plen = 0;
+               for (t = m; m; m = m->m_next)
+                       plen += m->m_len;
+               t->m_pkthdr.len = plen;
+       }
        return ((struct ip *)ip);
 
 dropfrag:
        return ((struct ip *)ip);
 
 dropfrag:
@@ -492,20 +611,25 @@ struct in_ifaddr *ip_rtaddr();
 
 /*
  * Do option processing on a datagram,
 
 /*
  * Do option processing on a datagram,
- * possibly discarding it if bad options
- * are encountered.
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
  */
  */
-ip_dooptions(ip, ifp)
-       register struct ip *ip;
-       struct ifnet *ifp;
+ip_dooptions(m)
+       struct mbuf *m;
 {
 {
+       register struct ip *ip = mtod(m, struct ip *);
+       register struct ip *ip = mtod(m, struct ip *);
        register u_char *cp;
        register u_char *cp;
-       int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB;
        register struct ip_timestamp *ipt;
        register struct in_ifaddr *ia;
        register struct ip_timestamp *ipt;
        register struct in_ifaddr *ia;
-       struct in_addr *sin;
+       int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
+       int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
+       struct in_addr *sin, dst;
        n_time ntime;
 
        n_time ntime;
 
+       dst = ip->ip_dst;
        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) {
@@ -533,7 +657,7 @@ ip_dooptions(ip, ifp)
                 * or do nothing if loosely routed.
                 * Record interface address and bring up next address
                 * component.  If strictly routed make sure next
                 * 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.
+                * address is on directly accessible net.
                 */
                case IPOPT_LSRR:
                case IPOPT_SSRR:
                 */
                case IPOPT_LSRR:
                case IPOPT_SSRR:
@@ -567,19 +691,29 @@ ip_dooptions(ip, ifp)
                        /*
                         * locate outgoing interface
                         */
                        /*
                         * locate outgoing interface
                         */
-                       bcopy(cp + off, (caddr_t)&ipaddr.sin_addr,
+                       bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
                            sizeof(ipaddr.sin_addr));
                            sizeof(ipaddr.sin_addr));
-                       if ((opt == IPOPT_SSRR &&
-                           in_iaonnetof(in_netof(ipaddr.sin_addr)) == 0) ||
-                           (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+                       if (opt == IPOPT_SSRR) {
+#define        INA     struct in_ifaddr *
+#define        SA      struct sockaddr *
+                           if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+                               ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+                       } else
+                               ia = ip_rtaddr(ipaddr.sin_addr);
+                       if (ia == 0) {
                                type = ICMP_UNREACH;
                                code = ICMP_UNREACH_SRCFAIL;
                                goto bad;
                        }
                        ip->ip_dst = ipaddr.sin_addr;
                                type = ICMP_UNREACH;
                                code = ICMP_UNREACH_SRCFAIL;
                                goto bad;
                        }
                        ip->ip_dst = ipaddr.sin_addr;
-                       bcopy(&(IA_SIN(ia)->sin_addr), cp + off,
-                               sizeof(struct in_addr));
+                       bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+                           (caddr_t)(cp + off), sizeof(struct in_addr));
                        cp[IPOPT_OFFSET] += sizeof(struct in_addr);
                        cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+                       forward = 1;
+                       /*
+                        * Let ip_intr's mcast routing check handle mcast pkts
+                        */
+                       forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
                        break;
 
                case IPOPT_RR:
                        break;
 
                case IPOPT_RR:
@@ -593,18 +727,20 @@ ip_dooptions(ip, ifp)
                        off--;                  /* 0 origin */
                        if (off > optlen - sizeof(struct in_addr))
                                break;
                        off--;                  /* 0 origin */
                        if (off > optlen - sizeof(struct in_addr))
                                break;
-                       bcopy(cp + off, (caddr_t)&ipaddr.sin_addr,
+                       bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
                            sizeof(ipaddr.sin_addr));
                        /*
                            sizeof(ipaddr.sin_addr));
                        /*
-                        * locate outgoing interface
+                        * locate outgoing interface; if we're the destination,
+                        * use the incoming interface (should be same).
                         */
                         */
-                       if ((ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+                       if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+                           (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
                                type = ICMP_UNREACH;
                                type = ICMP_UNREACH;
-                               code = ICMP_UNREACH_SRCFAIL;
+                               code = ICMP_UNREACH_HOST;
                                goto bad;
                        }
                                goto bad;
                        }
-                       bcopy(&(IA_SIN(ia)->sin_addr), cp + off,
-                               sizeof(struct in_addr));
+                       bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+                           (caddr_t)(cp + off), sizeof(struct in_addr));
                        cp[IPOPT_OFFSET] += sizeof(struct in_addr);
                        break;
 
                        cp[IPOPT_OFFSET] += sizeof(struct in_addr);
                        break;
 
@@ -618,7 +754,7 @@ ip_dooptions(ip, ifp)
                                        goto bad;
                                break;
                        }
                                        goto bad;
                                break;
                        }
-                       sin = (struct in_addr *)(cp+cp[IPOPT_OFFSET]-1);
+                       sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
                        switch (ipt->ipt_flg) {
 
                        case IPOPT_TS_TSONLY:
                        switch (ipt->ipt_flg) {
 
                        case IPOPT_TS_TSONLY:
@@ -628,21 +764,24 @@ ip_dooptions(ip, ifp)
                                if (ipt->ipt_ptr + sizeof(n_time) +
                                    sizeof(struct in_addr) > ipt->ipt_len)
                                        goto bad;
                                if (ipt->ipt_ptr + sizeof(n_time) +
                                    sizeof(struct in_addr) > ipt->ipt_len)
                                        goto bad;
-                               if (in_ifaddr == 0)
-                                       goto bad;       /* ??? */
-                               bcopy((caddr_t)&IA_SIN(in_ifaddr)->sin_addr,
+                               ipaddr.sin_addr = dst;
+                               ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
+                                                           m->m_pkthdr.rcvif);
+                               if (ia == 0)
+                                       continue;
+                               bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
                                    (caddr_t)sin, sizeof(struct in_addr));
                                    (caddr_t)sin, sizeof(struct in_addr));
-                               sin++;
+                               ipt->ipt_ptr += sizeof(struct in_addr);
                                break;
 
                        case IPOPT_TS_PRESPEC:
                                break;
 
                        case IPOPT_TS_PRESPEC:
-                               bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
-                                   sizeof(struct in_addr));
-                               if (ifa_ifwithaddr((struct sockaddr *)&ipaddr) == 0)
-                                       continue;
                                if (ipt->ipt_ptr + sizeof(n_time) +
                                    sizeof(struct in_addr) > ipt->ipt_len)
                                        goto bad;
                                if (ipt->ipt_ptr + sizeof(n_time) +
                                    sizeof(struct in_addr) > ipt->ipt_len)
                                        goto bad;
+                               bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+                                   sizeof(struct in_addr));
+                               if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+                                       continue;
                                ipt->ipt_ptr += sizeof(struct in_addr);
                                break;
 
                                ipt->ipt_ptr += sizeof(struct in_addr);
                                break;
 
@@ -650,13 +789,20 @@ ip_dooptions(ip, ifp)
                                goto bad;
                        }
                        ntime = iptime();
                                goto bad;
                        }
                        ntime = iptime();
-                       bcopy((caddr_t)&ntime, (caddr_t)sin, sizeof(n_time));
+                       bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+                           sizeof(n_time));
                        ipt->ipt_ptr += sizeof(n_time);
                }
        }
                        ipt->ipt_ptr += sizeof(n_time);
                }
        }
+       if (forward) {
+               ip_forward(m, 1);
+               return (1);
+       }
        return (0);
 bad:
        return (0);
 bad:
-       icmp_error(ip, type, code, ifp);
+       ip->ip_len -= ip->ip_hl << 2;   /* XXX icmp_error adds in hdr length */
+       icmp_error(m, type, code, 0, 0);
+       ipstat.ips_badoptions++;
        return (1);
 }
 
        return (1);
 }
 
@@ -669,7 +815,6 @@ ip_rtaddr(dst)
         struct in_addr dst;
 {
        register struct sockaddr_in *sin;
         struct in_addr dst;
 {
        register struct sockaddr_in *sin;
-       register struct in_ifaddr *ia;
 
        sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
 
 
        sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
 
@@ -679,19 +824,14 @@ ip_rtaddr(dst)
                        ipforward_rt.ro_rt = 0;
                }
                sin->sin_family = AF_INET;
                        ipforward_rt.ro_rt = 0;
                }
                sin->sin_family = AF_INET;
+               sin->sin_len = sizeof(*sin);
                sin->sin_addr = dst;
 
                rtalloc(&ipforward_rt);
        }
        if (ipforward_rt.ro_rt == 0)
                return ((struct in_ifaddr *)0);
                sin->sin_addr = dst;
 
                rtalloc(&ipforward_rt);
        }
        if (ipforward_rt.ro_rt == 0)
                return ((struct in_ifaddr *)0);
-       /*
-        * Find address associated with outgoing interface.
-        */
-       for (ia = in_ifaddr; ia; ia = ia->ia_next)
-               if (ia->ia_ifp == ipforward_rt.ro_rt->rt_ifp)
-                       break;
-       return (ia);
+       return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
 }
 
 /*
 }
 
 /*
@@ -699,21 +839,21 @@ ip_rtaddr(dst)
  * to be picked up later by ip_srcroute if the receiver is interested.
  */
 save_rte(option, dst)
  * to be picked up later by ip_srcroute if the receiver is interested.
  */
 save_rte(option, dst)
-       caddr_t option;
+       u_char *option;
        struct in_addr dst;
 {
        struct in_addr dst;
 {
-       int olen;
-       extern ipprintfs;
+       unsigned olen;
 
        olen = option[IPOPT_OLEN];
 
        olen = option[IPOPT_OLEN];
-       if (olen > sizeof(ip_srcrt) - 1) {
-               if (ipprintfs)
-                       printf("save_rte: olen %d\n", olen);
+#ifdef DIAGNOSTIC
+       if (ipprintfs)
+               printf("save_rte: olen %d\n", olen);
+#endif
+       if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
                return;
                return;
-       }
-       bcopy(option, (caddr_t)ip_srcrt.srcopt, olen);
+       bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
        ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
        ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
-       ip_srcrt.route[ip_nhops++] = dst;
+       ip_srcrt.dst = dst;
 }
 
 /*
 }
 
 /*
@@ -729,29 +869,76 @@ ip_srcroute()
 
        if (ip_nhops == 0)
                return ((struct mbuf *)0);
 
        if (ip_nhops == 0)
                return ((struct mbuf *)0);
-       m = m_get(M_WAIT, MT_SOOPTS);
-       m->m_len = ip_nhops * sizeof(struct in_addr) + IPOPT_OFFSET + 1 + 1;
+       m = m_get(M_DONTWAIT, MT_SOOPTS);
+       if (m == 0)
+               return ((struct mbuf *)0);
+
+#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
+
+       /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
+       m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
+           OPTSIZ;
+       if (ipprintfs)
+               printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
+
+#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
+
+       /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
+       m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
+           OPTSIZ;
+#ifdef DIAGNOSTIC
+       if (ipprintfs)
+               printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
+#endif
 
        /*
         * First save first hop for return route
         */
        p = &ip_srcrt.route[ip_nhops - 1];
        *(mtod(m, struct in_addr *)) = *p--;
 
        /*
         * First save first hop for return route
         */
        p = &ip_srcrt.route[ip_nhops - 1];
        *(mtod(m, struct in_addr *)) = *p--;
+#ifdef DIAGNOSTIC
+       if (ipprintfs)
+               printf(" hops %X", ntohl(*mtod(m, struct in_addr *)));
+       if (ipprintfs)
+               printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
+#endif
 
        /*
         * Copy option fields and padding (nop) to mbuf.
         */
        ip_srcrt.nop = IPOPT_NOP;
 
        /*
         * Copy option fields and padding (nop) to mbuf.
         */
        ip_srcrt.nop = IPOPT_NOP;
-       bcopy((caddr_t)&ip_srcrt, mtod(m, caddr_t) + sizeof(struct in_addr),
-           IPOPT_OFFSET + 1 + 1);
+       ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
+       bcopy((caddr_t)&ip_srcrt.nop,
+           mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
        q = (struct in_addr *)(mtod(m, caddr_t) +
        q = (struct in_addr *)(mtod(m, caddr_t) +
-           sizeof(struct in_addr) + IPOPT_OFFSET + 1 + 1);
+           sizeof(struct in_addr) + OPTSIZ);
+#undef OPTSIZ
        /*
         * Record return path as an IP source route,
         * reversing the path (pointers are now aligned).
         */
        /*
         * Record return path as an IP source route,
         * reversing the path (pointers are now aligned).
         */
-       while (p >= ip_srcrt.route)
+       while (p >= ip_srcrt.route) {
+#ifdef DIAGNOSTIC
+               if (ipprintfs)
+                       printf(" %lx", ntohl(q->s_addr));
+#endif
                *q++ = *p--;
                *q++ = *p--;
+       }
+       /*
+        * Last hop goes to final destination.
+        */
+       *q = ip_srcrt.dst;
+       if (ipprintfs)
+               printf(" %X\n", ntohl(*q));
+       }
+       /*
+        * Last hop goes to final destination.
+        */
+       *q = ip_srcrt.dst;
+#ifdef DIAGNOSTIC
+       if (ipprintfs)
+               printf(" %lx\n", ntohl(q->s_addr));
+#endif
        return (m);
 }
 
        return (m);
 }
 
@@ -760,188 +947,194 @@ ip_srcroute()
  * level protocol in the kernel.
  * Second argument is buffer to which options
  * will be moved, and return value is their length.
  * level protocol in the kernel.
  * Second argument is buffer to which options
  * will be moved, and return value is their length.
+#ifdef NEW
+ * XXX should be deleted; last arg currently ignored.
+#endif NEW
+ * XXX should be deleted; last arg currently ignored.
  */
  */
-ip_stripoptions(ip, mopt)
-       struct ip *ip;
+ip_stripoptions(m, mopt)
+       register struct mbuf *m;
        struct mbuf *mopt;
 {
        register int i;
        struct mbuf *mopt;
 {
        register int i;
-       register struct mbuf *m;
+       struct ip *ip = mtod(m, struct ip *);
        register caddr_t opts;
        int olen;
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
        register caddr_t opts;
        int olen;
 
        olen = (ip->ip_hl<<2) - sizeof (struct ip);
-       m = dtom(ip);
        opts = (caddr_t)(ip + 1);
        opts = (caddr_t)(ip + 1);
-       if (mopt) {
-               mopt->m_len = olen;
-               mopt->m_off = MMINOFF;
-               bcopy(opts, mtod(mopt, caddr_t), (unsigned)olen);
-       }
        i = m->m_len - (sizeof (struct ip) + olen);
        bcopy(opts  + olen, opts, (unsigned)i);
        m->m_len -= olen;
        i = m->m_len - (sizeof (struct ip) + olen);
        bcopy(opts  + olen, opts, (unsigned)i);
        m->m_len -= olen;
+       if (m->m_flags & M_PKTHDR)
+               m->m_pkthdr.len -= olen;
        ip->ip_hl = sizeof(struct ip) >> 2;
 }
 
 u_char inetctlerrmap[PRC_NCMDS] = {
        0,              0,              0,              0,
        ip->ip_hl = sizeof(struct ip) >> 2;
 }
 
 u_char inetctlerrmap[PRC_NCMDS] = {
        0,              0,              0,              0,
-       0,              0,              EHOSTDOWN,      EHOSTUNREACH,
-       ENETUNREACH,    EHOSTUNREACH,   ECONNREFUSED,   ECONNREFUSED,
+       0,              EMSGSIZE,       EHOSTDOWN,      EHOSTUNREACH,
+       EHOSTUNREACH,   EHOSTUNREACH,   ECONNREFUSED,   ECONNREFUSED,
        EMSGSIZE,       EHOSTUNREACH,   0,              0,
        0,              0,              0,              0,
        ENOPROTOOPT
 };
 
        EMSGSIZE,       EHOSTUNREACH,   0,              0,
        0,              0,              0,              0,
        ENOPROTOOPT
 };
 
-#ifndef        IPFORWARDING
-#define        IPFORWARDING    1
-#endif
-#ifndef        IPSENDREDIRECTS
-#define        IPSENDREDIRECTS 1
-#endif
-int    ipprintfs = 0;
-int    ipforwarding = IPFORWARDING;
-extern int in_interfaces;
-int    ipsendredirects = IPSENDREDIRECTS;
-
 /*
  * Forward a packet.  If some error occurs return the sender
  * an icmp packet.  Note we can't always generate a meaningful
  * icmp message because icmp doesn't have a large enough repertoire
  * of codes and types.
 /*
  * Forward a packet.  If some error occurs return the sender
  * an icmp packet.  Note we can't always generate a meaningful
  * icmp message because icmp doesn't have a large enough repertoire
  * of codes and types.
+ *
+ * If not forwarding, just drop the packet.  This could be confusing
+ * if ipforwarding was zero but some routing protocol was advancing
+ * us as a gateway to somewhere.  However, we must let the routing
+ * protocol deal with that.
+ *
+ * The srcrt parameter indicates whether the packet is being forwarded
+ * via a source route.
  */
  */
-ip_forward(ip, ifp)
-       register struct ip *ip;
-       struct ifnet *ifp;
+ip_forward(m, srcrt)
+       struct mbuf *m;
+       int srcrt;
 {
 {
-       register int error, type = 0, code;
+       register struct ip *ip = mtod(m, struct ip *);
+       register struct ip *ip = mtod(m, struct ip *);
        register struct sockaddr_in *sin;
        register struct sockaddr_in *sin;
+       register struct rtentry *rt;
+       int error, type = 0, code;
        struct mbuf *mcopy;
        struct in_addr dest;
        struct mbuf *mcopy;
        struct in_addr dest;
+       struct ifnet *destifp;
 
 
-#ifdef lint
        dest.s_addr = 0;
        dest.s_addr = 0;
-#endif
+#ifdef DIAGNOSTIC
        if (ipprintfs)
                printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
                        ip->ip_dst, ip->ip_ttl);
        if (ipprintfs)
                printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
                        ip->ip_dst, ip->ip_ttl);
-       ip->ip_id = htons(ip->ip_id);
-       if (ipforwarding == 0 || in_interfaces <= 1) {
-               /* can't tell difference between net and host */
-               type = ICMP_UNREACH, code = ICMP_UNREACH_NET;
-               goto sendicmp;
+#endif
+       if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
+               ipstat.ips_cantforward++;
+               m_freem(m);
+               return;
        }
        }
-       if (ip->ip_ttl < IPTTLDEC) {
-               type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
-               goto sendicmp;
+       ip->ip_id = htons(ip->ip_id);
+       if (ip->ip_ttl <= IPTTLDEC) {
+               icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest);
+               return;
        }
        ip->ip_ttl -= IPTTLDEC;
 
        }
        ip->ip_ttl -= IPTTLDEC;
 
-       /*
-        * 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));
-
        sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
        sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
-       if (ipforward_rt.ro_rt == 0 ||
+       if ((rt = ipforward_rt.ro_rt) == 0 ||
            ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
                if (ipforward_rt.ro_rt) {
                        RTFREE(ipforward_rt.ro_rt);
                        ipforward_rt.ro_rt = 0;
                }
                sin->sin_family = AF_INET;
            ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
                if (ipforward_rt.ro_rt) {
                        RTFREE(ipforward_rt.ro_rt);
                        ipforward_rt.ro_rt = 0;
                }
                sin->sin_family = AF_INET;
+               sin->sin_len = sizeof(*sin);
                sin->sin_addr = ip->ip_dst;
 
                rtalloc(&ipforward_rt);
                sin->sin_addr = ip->ip_dst;
 
                rtalloc(&ipforward_rt);
+               if (ipforward_rt.ro_rt == 0) {
+                       icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest);
+                       return;
+               }
+               rt = ipforward_rt.ro_rt;
        }
        }
+
+       /*
+        * Save at most 64 bytes of the packet in case
+        * we need to generate an ICMP message to the src.
+        */
+       mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
+
+#ifdef GATEWAY
+       ip_ifmatrix[rt->rt_ifp->if_index +
+            if_index * m->m_pkthdr.rcvif->if_index]++;
+#endif
        /*
         * If forwarding packet using same interface that it came in on,
         * perhaps should send a redirect to sender to shortcut a hop.
         * Only send redirect if source is sending directly to us,
         * and if packet was not source routed (or has any options).
        /*
         * If forwarding packet using same interface that it came in on,
         * perhaps should send a redirect to sender to shortcut a hop.
         * Only send redirect if source is sending directly to us,
         * and if packet was not source routed (or has any options).
+        * Also, don't send redirect if forwarding using a default route
+        * or a route modified by a redirect.
         */
         */
-       if (ipforward_rt.ro_rt && ipforward_rt.ro_rt->rt_ifp == ifp &&
-           ipsendredirects && ip->ip_hl == (sizeof(struct ip) >> 2)) {
-               struct in_ifaddr *ia;
-               extern struct in_ifaddr *ifptoia();
+#define        satosin(sa)     ((struct sockaddr_in *)(sa))
+       if (rt->rt_ifp == m->m_pkthdr.rcvif &&
+           (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
+           satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
+           ipsendredirects && !srcrt) {
+#define        RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa))
                u_long src = ntohl(ip->ip_src.s_addr);
                u_long dst = ntohl(ip->ip_dst.s_addr);
 
                u_long src = ntohl(ip->ip_src.s_addr);
                u_long dst = ntohl(ip->ip_dst.s_addr);
 
-               if ((ia = ifptoia(ifp)) &&
-                  (src & ia->ia_subnetmask) == ia->ia_subnet) {
-                   if (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY)
-                       dest = satosin(&ipforward_rt.ro_rt->rt_gateway)->sin_addr;
+               if (RTA(rt) &&
+                   (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
+                   if (rt->rt_flags & RTF_GATEWAY)
+                       dest = satosin(rt->rt_gateway)->sin_addr;
                    else
                        dest = ip->ip_dst;
                    else
                        dest = ip->ip_dst;
-                   /*
-                    * If the destination is reached by a route to host,
-                    * is directly on the attached net (!),
-                    * or if the destination is on a subnet of a local net
-                    * not known to the source net, use host redirect.
-                    * (We may be the correct first hop for other subnets.)
-                    */
+                   /* Router requirements says to only send host redirects */
                    type = ICMP_REDIRECT;
                    type = ICMP_REDIRECT;
-                   code = ICMP_REDIRECT_NET;
-                   if ((ipforward_rt.ro_rt->rt_flags & RTF_HOST) ||
-                      (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY) == 0)
-                       code = ICMP_REDIRECT_HOST;
-                   else for (ia = in_ifaddr; ia = ia->ia_next; )
-                       if ((dst & ia->ia_netmask) == ia->ia_net) {
-                           if ((src & ia->ia_netmask) != ia->ia_net)
-                               code = ICMP_REDIRECT_HOST;
-                           break;
-                       }
+                   code = ICMP_REDIRECT_HOST;
+#ifdef DIAGNOSTIC
                    if (ipprintfs)
                    if (ipprintfs)
-                       printf("redirect (%d) to %x\n", code, dest);
+                       printf("redirect (%d) to %x\n", code, dest.s_addr);
+#endif
                }
        }
 
                }
        }
 
-       error = ip_output(dtom(ip), (struct mbuf *)0, &ipforward_rt,
-               IP_FORWARDING);
+       error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING
+#ifdef DIRECTED_BROADCAST
+                           | IP_ALLOWBROADCAST
+#endif
+                                               , 0);
        if (error)
                ipstat.ips_cantforward++;
        if (error)
                ipstat.ips_cantforward++;
-       else if (type)
-               ipstat.ips_redirectsent++;
        else {
        else {
-               if (mcopy)
-                       m_freem(mcopy);
                ipstat.ips_forward++;
                ipstat.ips_forward++;
-               return;
+               if (type)
+                       ipstat.ips_redirectsent++;
+               else {
+                       if (mcopy)
+                               m_freem(mcopy);
+                       return;
+               }
        }
        if (mcopy == NULL)
                return;
        }
        if (mcopy == NULL)
                return;
-       ip = mtod(mcopy, struct ip *);
-       type = ICMP_UNREACH;
+       destifp = NULL;
+
        switch (error) {
 
        case 0:                         /* forwarded, but need redirect */
        switch (error) {
 
        case 0:                         /* forwarded, but need redirect */
-               type = ICMP_REDIRECT;
-               /* code set above */
+               /* type, code set above */
                break;
 
                break;
 
-       case ENETUNREACH:
+       case ENETUNREACH:               /* shouldn't happen, checked above */
+       case EHOSTUNREACH:
        case ENETDOWN:
        case ENETDOWN:
-               code = ICMP_UNREACH_NET;
+       case EHOSTDOWN:
+       default:
+               type = ICMP_UNREACH;
+               code = ICMP_UNREACH_HOST;
                break;
 
        case EMSGSIZE:
                break;
 
        case EMSGSIZE:
+               type = ICMP_UNREACH;
                code = ICMP_UNREACH_NEEDFRAG;
                code = ICMP_UNREACH_NEEDFRAG;
-               break;
-
-       case EPERM:
-               code = ICMP_UNREACH_PORT;
+               if (ipforward_rt.ro_rt)
+                       destifp = ipforward_rt.ro_rt->rt_ifp;
+               ipstat.ips_cantfrag++;
                break;
 
        case ENOBUFS:
                type = ICMP_SOURCEQUENCH;
                break;
 
        case ENOBUFS:
                type = ICMP_SOURCEQUENCH;
-               break;
-
-       case EHOSTDOWN:
-       case EHOSTUNREACH:
-               code = ICMP_UNREACH_HOST;
+               code = 0;
                break;
        }
                break;
        }
-sendicmp:
-       icmp_error(ip, type, code, ifp, dest);
+       icmp_error(mcopy, type, code, dest, destifp);
 }
 }