4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / sys / netinet / ip_icmp.c
index 1039697..33f4c25 100644 (file)
-/*     ip_icmp.c       4.15    82/04/25        */
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/mbuf.h"
-#include "../h/protosw.h"
-#include "../h/socket.h"
-#include "../h/clock.h"
-#include "../net/in.h"
-#include "../net/in_systm.h"
-#include "../net/ip.h"
-#include "../net/ip_icmp.h"
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ *
+ *     @(#)ip_icmp.c   8.1 (Berkeley) %G%
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.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/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
 
 /*
  * ICMP routines: error generation, receive packet processing, and
  * routines to turnaround packets back to the originator, and
  * host table maintenance routines.
  */
 
 /*
  * ICMP routines: error generation, receive packet processing, and
  * routines to turnaround packets back to the originator, and
  * host table maintenance routines.
  */
-int    icmpprintfs = 1;
+
+int    icmpmaskrepl = 0;
+#ifdef ICMPPRINTFS
+int    icmpprintfs = 0;
+#endif
+
+extern struct protosw inetsw[];
 
 /*
  * Generate an error packet of type error
  * in response to bad packet ip.
  */
 
 /*
  * Generate an error packet of type error
  * in response to bad packet ip.
  */
-icmp_error(oip, type, code)
-       struct ip *oip;
+void
+icmp_error(n, type, code, dest, destifp)
+       struct mbuf *n;
        int type, code;
        int type, code;
+       n_long dest;
+       struct ifnet *destifp;
 {
 {
+       register struct ip *oip = mtod(n, struct ip *), *nip;
        register unsigned oiplen = oip->ip_hl << 2;
        register struct icmp *icp;
        register unsigned oiplen = oip->ip_hl << 2;
        register struct icmp *icp;
-       struct mbuf *m;
-       struct ip *nip;
-COUNT(ICMP_ERROR);
+       register struct mbuf *m;
+       unsigned icmplen;
 
 
+#ifdef ICMPPRINTFS
        if (icmpprintfs)
                printf("icmp_error(%x, %d, %d)\n", oip, type, code);
        if (icmpprintfs)
                printf("icmp_error(%x, %d, %d)\n", oip, type, code);
+#endif
+       if (type != ICMP_REDIRECT)
+               icmpstat.icps_error++;
        /*
        /*
-        * Make sure that the old IP packet had 8 bytes of data to return;
-        * if not, don't bother.  Also don't EVER error if the old
-        * packet protocol was ICMP.
+        * Don't send error if not the first fragment of message.
+        * Don't error if the old packet protocol was ICMP
+        * error message, only known informational types.
         */
         */
-       if (oip->ip_len < 8 || oip->ip_p == IPPROTO_ICMP)
-               goto free;
-
+       if (oip->ip_off &~ (IP_MF|IP_DF))
+               goto freeit;
+       if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
+         dtom(oip)->m_len >= oiplen + ICMP_MINLEN &&
+         n->m_len >= oiplen + ICMP_MINLEN &&
+         !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
+               icmpstat.icps_oldicmp++;
+               goto freeit;
+       }
+       /* Don't send error in response to a multicast or broadcast packet */
+       if (n->m_flags & (M_BCAST|M_MCAST))
+               goto freeit;
        /*
         * First, formulate icmp message
         */
        /*
         * First, formulate icmp message
         */
-       m = m_get(M_DONTWAIT);
-       if (m == 0)
-               goto free;
-       m->m_len = oiplen + 8 + ICMP_MINLEN;
-       m->m_off = MMAXOFF - m->m_len;
+       m = m_gethdr(M_DONTWAIT, MT_HEADER);
+       if (m == NULL)
+               goto freeit;
+       icmplen = oiplen + min(8, oip->ip_len);
+       m->m_len = icmplen + ICMP_MINLEN;
+       MH_ALIGN(m, m->m_len);
        icp = mtod(m, struct icmp *);
        icp = mtod(m, struct icmp *);
+       if ((u_int)type > ICMP_MAXTYPE)
+               panic("icmp_error");
+       icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
        icp->icmp_type = type;
-       icp->icmp_void = 0;
-       if (type == ICMP_PARAMPROB) {
-               icp->icmp_pptr = code;
-               code = 0;
+       if (type == ICMP_REDIRECT)
+               icp->icmp_gwaddr.s_addr = dest;
+       else {
+               icp->icmp_void = 0;
+               /* 
+                * The following assignments assume an overlay with the
+                * zeroed icmp_void field.
+                */
+               if (type == ICMP_PARAMPROB) {
+                       icp->icmp_pptr = code;
+                       code = 0;
+               } else if (type == ICMP_UNREACH &&
+                       code == ICMP_UNREACH_NEEDFRAG && destifp) {
+                       icp->icmp_nextmtu = htons(destifp->if_mtu);
+               }
        }
        }
+
        icp->icmp_code = code;
        icp->icmp_code = code;
-       bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, oiplen + 8);
+       bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
        nip = &icp->icmp_ip;
        nip = &icp->icmp_ip;
-       nip->ip_len += oiplen;
-#if vax || pdp11
-       nip->ip_len = htons((u_short)nip->ip_len);
-#endif
+       nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
 
        /*
 
        /*
-        * Now, copy old ip header in front of icmp
-        * message.  This allows us to reuse any source
-        * routing info present.
+        * Now, copy old ip header (without options)
+        * in front of icmp message.
         */
         */
-       m->m_off -= oiplen;
+       if (m->m_data - sizeof(struct ip) < m->m_pktdat)
+               panic("icmp len");
+       m->m_data -= sizeof(struct ip);
+       m->m_len += sizeof(struct ip);
+       m->m_pkthdr.len = m->m_len;
+       m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
        nip = mtod(m, struct ip *);
        nip = mtod(m, struct ip *);
-       bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
-       nip->ip_len = m->m_len + oiplen;
+       bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip));
+       nip->ip_len = m->m_len;
+       nip->ip_hl = sizeof(struct ip) >> 2;
+       nip->ip_hl = sizeof(struct ip) >> 2;
        nip->ip_p = IPPROTO_ICMP;
        nip->ip_p = IPPROTO_ICMP;
-       /* icmp_send adds ip header to m_off and m_len, so we deduct here */
-       m->m_off += oiplen;
-       icmp_reflect(nip);
+       nip->ip_tos = 0;
+       icmp_reflect(m);
 
 
-free:
-       m_freem(dtom(oip));
+freeit:
+       m_freem(n);
 }
 
 }
 
-static char icmpmap[] = {
-       -1,              -1,            -1,
-       PRC_UNREACH_NET, PRC_QUENCH,    PRC_REDIRECT_NET,
-       -1,              -1,            -1,
-       -1,              -1,            PRC_TIMXCEED_INTRANS,
-       PRC_PARAMPROB,   -1,            -1,
-       -1,              -1
-};
-
 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
-static struct sockaddr_in icmpsrc = { AF_INET };
-static struct sockaddr_in icmpdst = { AF_INET };
+static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
+static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
+static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
+struct sockaddr_in icmpmask = { 8, 0 };
 
 /*
  * Process a received ICMP message.
  */
 
 /*
  * Process a received ICMP message.
  */
-icmp_input(m)
-       struct mbuf *m;
+void
+icmp_input(m, hlen)
+       register struct mbuf *m;
+       int hlen;
 {
        register struct icmp *icp;
        register struct ip *ip = mtod(m, struct ip *);
 {
        register struct icmp *icp;
        register struct ip *ip = mtod(m, struct ip *);
-       int icmplen = ip->ip_len, hlen = ip->ip_hl << 2, i, (*ctlfunc)();
+       int icmplen = ip->ip_len;
+       register int i;
+       struct in_ifaddr *ia;
+       void (*ctlfunc) __P((int, struct sockaddr *, struct ip *));
+       int code;
        extern u_char ip_protox[];
        extern u_char ip_protox[];
-COUNT(ICMP_INPUT);
 
        /*
         * Locate icmp structure in mbuf, and check
         * that not corrupted and of at least minimum length.
         */
 
        /*
         * Locate icmp structure in mbuf, and check
         * that not corrupted and of at least minimum length.
         */
+#ifdef ICMPPRINTFS
        if (icmpprintfs)
        if (icmpprintfs)
-               printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
-       if (icmplen < ICMP_MINLEN)
-               goto free;
+               printf("icmp_input from %x to %x, len %d\n",
+                       ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr),
+                       icmplen);
+#endif
+       if (icmplen < ICMP_MINLEN) {
+               icmpstat.icps_tooshort++;
+               goto freeit;
+       }
+       i = hlen + min(icmplen, ICMP_ADVLENMIN);
+       if (m->m_len < i && (m = m_pullup(m, i)) == 0)  {
+               icmpstat.icps_tooshort++;
+               return;
+       }
+       ip = mtod(m, struct ip *);
        m->m_len -= hlen;
        m->m_len -= hlen;
-       m->m_off += hlen;
-       /* need routine to make sure header is in this mbuf here */
+       m->m_data += hlen;
        icp = mtod(m, struct icmp *);
        icp = mtod(m, struct icmp *);
-       i = icp->icmp_cksum;
-       icp->icmp_cksum = 0;
-       if (i != in_cksum(m, icmplen)) {
-               printf("icmp: cksum %x\n", i);
-               goto free;
+       if (in_cksum(m, icmplen)) {
+               icmpstat.icps_checksum++;
+               goto freeit;
        }
        }
+       m->m_len += hlen;
+       m->m_data -= hlen;
 
 
+#ifdef ICMPPRINTFS
        /*
         * Message type specific processing.
         */
        if (icmpprintfs)
                printf("icmp_input, type %d code %d\n", icp->icmp_type,
        /*
         * Message type specific processing.
         */
        if (icmpprintfs)
                printf("icmp_input, type %d code %d\n", icp->icmp_type,
-                       icp->icmp_code);
-       switch (i = icp->icmp_type) {
+                   icp->icmp_code);
+#endif
+       if (icp->icmp_type > ICMP_MAXTYPE)
+               goto raw;
+       icmpstat.icps_inhist[icp->icmp_type]++;
+       code = icp->icmp_code;
+       switch (icp->icmp_type) {
 
        case ICMP_UNREACH:
 
        case ICMP_UNREACH:
+               switch (code) {
+                       case ICMP_UNREACH_NET:
+                       case ICMP_UNREACH_HOST:
+                       case ICMP_UNREACH_PROTOCOL:
+                       case ICMP_UNREACH_PORT:
+                       case ICMP_UNREACH_SRCFAIL:
+                               code += PRC_UNREACH_NET;
+                               break;
+
+                       case ICMP_UNREACH_NEEDFRAG:
+                               code = PRC_MSGSIZE;
+                               break;
+                               
+                       case ICMP_UNREACH_NET_UNKNOWN:
+                       case ICMP_UNREACH_NET_PROHIB:
+                       case ICMP_UNREACH_TOSNET:
+                               code = PRC_UNREACH_NET;
+                               break;
+
+                       case ICMP_UNREACH_HOST_UNKNOWN:
+                       case ICMP_UNREACH_ISOLATED:
+                       case ICMP_UNREACH_HOST_PROHIB:
+                       case ICMP_UNREACH_TOSHOST:
+                               code = PRC_UNREACH_HOST;
+                               break;
+
+                       default:
+                               goto badcode;
+               }
+               goto deliver;
+
        case ICMP_TIMXCEED:
        case ICMP_TIMXCEED:
+               if (code > 1)
+                       goto badcode;
+               code += PRC_TIMXCEED_INTRANS;
+               goto deliver;
+
        case ICMP_PARAMPROB:
        case ICMP_PARAMPROB:
-       case ICMP_REDIRECT:
+               if (code > 1)
+                       goto badcode;
+               code = PRC_PARAMPROB;
+               goto deliver;
+
        case ICMP_SOURCEQUENCH:
        case ICMP_SOURCEQUENCH:
+               if (code)
+                       goto badcode;
+               code = PRC_QUENCH;
+       deliver:
                /*
                /*
-                * Problem with previous datagram; advise
-                * higher level routines.
+                * Problem with datagram; advise higher level routines.
                 */
                 */
-#if vax || pdp11
-               icp->icmp_ip.ip_len = ntohs(icp->icmp_ip.ip_len);
-#endif
-               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
-                       goto free;
+               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
+                   icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
+                       icmpstat.icps_badlen++;
+                       goto freeit;
+               }
+               NTOHS(icp->icmp_ip.ip_len);
+#ifdef ICMPPRINTFS
                if (icmpprintfs)
                        printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
                if (icmpprintfs)
                        printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
-               if (ctlfunc = protosw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
-                       (*ctlfunc)(icmpmap[i] + icp->icmp_code, (caddr_t)icp);
-               goto free;
+#endif
+               icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+               if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
+                       (*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
+                           &icp->icmp_ip);
+               break;
+
+       badcode:
+               icmpstat.icps_badcode++;
+               break;
 
        case ICMP_ECHO:
                icp->icmp_type = ICMP_ECHOREPLY;
                goto reflect;
 
        case ICMP_TSTAMP:
 
        case ICMP_ECHO:
                icp->icmp_type = ICMP_ECHOREPLY;
                goto reflect;
 
        case ICMP_TSTAMP:
-               if (icmplen < ICMP_TSLEN)
-                       goto free;
+               if (icmplen < ICMP_TSLEN) {
+                       icmpstat.icps_badlen++;
+                       break;
+               }
                icp->icmp_type = ICMP_TSTAMPREPLY;
                icp->icmp_rtime = iptime();
                icp->icmp_ttime = icp->icmp_rtime;      /* bogus, do later! */
                goto reflect;
                
                icp->icmp_type = ICMP_TSTAMPREPLY;
                icp->icmp_rtime = iptime();
                icp->icmp_ttime = icp->icmp_rtime;      /* bogus, do later! */
                goto reflect;
                
-       case ICMP_IREQ:
-               /* fill in source address zero fields! */
-               goto reflect;
+       case ICMP_MASKREQ:
+#define        satosin(sa)     ((struct sockaddr_in *)(sa))
+               if (icmpmaskrepl == 0)
+                       break;
+               /*
+                * We are not able to respond with all ones broadcast
+                * unless we receive it over a point-to-point interface.
+                */
+               if (icmplen < ICMP_MASKLEN)
+                       break;
+               switch (ip->ip_dst.s_addr) {
+
+               case INADDR_BROADCAST:
+               case INADDR_ANY:
+                       icmpdst.sin_addr = ip->ip_src;
+                       break;
+
+               default:
+                       icmpdst.sin_addr = ip->ip_dst;
+               }
+               ia = (struct in_ifaddr *)ifaof_ifpforaddr(
+                           (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
+               if (ia == 0)
+                       break;
+               icp->icmp_type = ICMP_MASKREPLY;
+               icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
+               if (ip->ip_src.s_addr == 0) {
+                       if (ia->ia_ifp->if_flags & IFF_BROADCAST)
+                           ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
+                       else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
+                           ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
+               }
+reflect:
+               ip->ip_len += hlen;     /* since ip_input deducts this */
+               icmpstat.icps_reflect++;
+               icmpstat.icps_outhist[icp->icmp_type]++;
+               icmp_reflect(m);
+               return;
+
+       case ICMP_REDIRECT:
+               if (code > 3)
+                       goto badcode;
+               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
+                   icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
+                       icmpstat.icps_badlen++;
+                       break;
+               }
+               /*
+                * Short circuit routing redirects to force
+                * immediate change in the kernel's routing
+                * tables.  The message is also handed to anyone
+                * listening on a raw socket (e.g. the routing
+                * daemon for use in updating its tables).
+                */
+               icmpgw.sin_addr = ip->ip_src;
+               icmpdst.sin_addr = icp->icmp_gwaddr;
+#ifdef ICMPPRINTFS
+               if (icmpprintfs)
+                       printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
+                               icp->icmp_gwaddr);
+#endif
+               icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+               rtredirect((struct sockaddr *)&icmpsrc,
+                 (struct sockaddr *)&icmpdst,
+                 (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
+                 (struct sockaddr *)&icmpgw, (struct rtentry **)0);
+               pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc);
+               break;
 
 
+       /*
+        * No kernel processing for the following;
+        * just fall through to send to raw listener.
+        */
        case ICMP_ECHOREPLY:
        case ICMP_ECHOREPLY:
+       case ICMP_ROUTERADVERT:
+       case ICMP_ROUTERSOLICIT:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
-               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp))
-                       goto free;
-               icmpsrc.sin_addr = ip->ip_src;
-               icmpdst.sin_addr = ip->ip_dst;
-               raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
-                 (struct sockaddr *)&icmpdst);
-               goto free;
-
+       case ICMP_MASKREPLY:
        default:
        default:
-               goto free;
+               break;
        }
        }
-reflect:
-       icmp_reflect(ip);
-free:
-       m_freem(dtom(ip));
+
+raw:
+       rip_input(m);
+       return;
+
+freeit:
+       m_freem(m);
 }
 
 /*
  * Reflect the ip packet back to the source
 }
 
 /*
  * Reflect the ip packet back to the source
- * TODO: rearrange ip source routing options.
  */
  */
-icmp_reflect(ip)
-       struct ip *ip;
+void
+icmp_reflect(m)
+       struct mbuf *m;
 {
 {
+       register struct ip *ip = mtod(m, struct ip *);
+       register struct in_ifaddr *ia;
        struct in_addr t;
        struct in_addr t;
-COUNT(ICMP_REFLECT);
+       struct mbuf *opts = 0, *ip_srcroute();
+       int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
 
 
+       if (!in_canforward(ip->ip_src) &&
+           ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) !=
+            (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
+               m_freem(m);     /* Bad return address */
+               goto done;      /* Ip_output() will check for broadcast */
+       }
        t = ip->ip_dst;
        ip->ip_dst = ip->ip_src;
        t = ip->ip_dst;
        ip->ip_dst = ip->ip_src;
+       /*
+        * If the incoming packet was addressed directly to us,
+        * use dst as the src for the reply.  Otherwise (broadcast
+        * or anonymous), use the address which corresponds
+        * to the incoming interface.
+        */
+       for (ia = in_ifaddr; ia; ia = ia->ia_next) {
+               if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
+                       break;
+               if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
+                   t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
+                       break;
+       }
+       icmpdst.sin_addr = t;
+       if (ia == (struct in_ifaddr *)0)
+               ia = (struct in_ifaddr *)ifaof_ifpforaddr(
+                       (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
+       /*
+        * The following happens if the packet was not addressed to us,
+        * and was received on an interface with no IP address.
+        */
+       if (ia == (struct in_ifaddr *)0)
+               ia = in_ifaddr;
+       t = IA_SIN(ia)->sin_addr;
        ip->ip_src = t;
        ip->ip_src = t;
-       icmp_send(ip);
-}
+       ip->ip_ttl = MAXTTL;
+
+       if (optlen > 0) {
+               register u_char *cp;
+               struct mbuf *m = dtom(ip);
+               int opt, cnt, off;
+               u_int len;
 
 
-int    generateicmpmsgs = 0;
+               register u_char *cp;
+               int opt, cnt;
+               u_int len;
+
+               /*
+                * Retrieve any source routing from the incoming packet;
+                * add on any record-route or timestamp options.
+                */
+               cp = (u_char *) (ip + 1);
+               if ((opts = ip_srcroute()) == 0 &&
+                   (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
+                       opts->m_len = sizeof(struct in_addr);
+                       mtod(opts, struct in_addr *)->s_addr = 0;
+               }
+               if (opts) {
+#ifdef ICMPPRINTFS
+                   if (icmpprintfs)
+                           printf("icmp_reflect optlen %d rt %d => ",
+                               optlen, opts->m_len);
+#endif
+                   for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
+                           opt = cp[IPOPT_OPTVAL];
+                           if (opt == IPOPT_EOL)
+                                   break;
+                           if (opt == IPOPT_NOP)
+                                   len = 1;
+                           else {
+                                   len = cp[IPOPT_OLEN];
+                                   if (len <= 0 || len > cnt)
+                                           break;
+                           }
+                           /*
+                            * Should check for overflow, but it "can't happen"
+                            */
+                           if (opt == IPOPT_RR || opt == IPOPT_TS || 
+                               opt == IPOPT_SECURITY) {
+                                   bcopy((caddr_t)cp,
+                                       mtod(opts, caddr_t) + opts->m_len, len);
+                                   opts->m_len += len;
+                           }
+                   }
+                   /* Terminate & pad, if necessary */
+                   if (cnt = opts->m_len % 4) {
+                           for (; cnt < 4; cnt++) {
+                                   *(mtod(opts, caddr_t) + opts->m_len) =
+                                       IPOPT_EOL;
+                                   opts->m_len++;
+                           }
+                   }
+#ifdef ICMPPRINTFS
+                   if (icmpprintfs)
+                           printf("%d\n", opts->m_len);
+#endif
+               }
+               /*
+                * Now strip out original options by copying rest of first
+                * mbuf's data back, and adjust the IP length.
+                */
+               ip->ip_len -= optlen;
+               ip->ip_hl = sizeof(struct ip) >> 2;
+               m->m_len -= optlen;
+               if (m->m_flags & M_PKTHDR)
+                       m->m_pkthdr.len -= optlen;
+               optlen += sizeof(struct ip);
+               bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
+                        (unsigned)(m->m_len - sizeof(struct ip)));
+       }
+       m->m_flags &= ~(M_BCAST|M_MCAST);
+
+       icmp_send(m, opts);
+done:
+       if (opts)
+               (void)m_free(opts);
+}
 
 /*
  * Send an icmp packet back to the ip level,
  * after supplying a checksum.
  */
 
 /*
  * Send an icmp packet back to the ip level,
  * after supplying a checksum.
  */
-icmp_send(ip)
-       struct ip *ip;
+void
+icmp_send(m, opts)
+       register struct mbuf *m;
+       struct mbuf *opts;
 {
 {
-       register int hlen = ip->ip_hl << 2;
+       register struct ip *ip = mtod(m, struct ip *);
+       register int hlen;
        register struct icmp *icp;
        register struct icmp *icp;
-       register struct mbuf *m = dtom(ip);
 
 
-COUNT(ICMP_SEND);
-       if (!generateicmpmsgs)
-               return;
+       hlen = ip->ip_hl << 2;
+       m->m_data += hlen;
+       m->m_len -= hlen;
        icp = mtod(m, struct icmp *);
        icp->icmp_cksum = 0;
        icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
        icp = mtod(m, struct icmp *);
        icp->icmp_cksum = 0;
        icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
-       m->m_off -= hlen;
+       m->m_data -= hlen;
        m->m_len += hlen;
        m->m_len += hlen;
+#ifdef ICMPPRINTFS
        if (icmpprintfs)
                printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
        if (icmpprintfs)
                printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
-       (void) ip_output(m, 0, 0, 0);
+#endif
+       (void) ip_output(m, opts, NULL, 0, NULL);
 }
 
 n_time
 iptime()
 {
 }
 
 n_time
 iptime()
 {
-       int s = spl6();
+       struct timeval atv;
        u_long t;
 
        u_long t;
 
-COUNT(IPTIME);
-       t = (time % SECDAY) * 1000 + lbolt * hz;
-       splx(s);
+       microtime(&atv);
+       t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
        return (htonl(t));
 }
        return (htonl(t));
 }
+
+int
+icmp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+       int *name;
+       u_int namelen;
+       void *oldp;
+       size_t *oldlenp;
+       void *newp;
+       size_t newlen;
+{
+       extern int ip_ttl;
+
+       /* all sysctl names at this level are terminal */
+       if (namelen != 1)
+               return (ENOTDIR);
+
+       switch (name[0]) {
+       case ICMPCTL_MASKREPL:
+               return (sysctl_int(oldp, oldlenp, newp, newlen, &icmpmaskrepl));
+       default:
+               return (ENOPROTOOPT);
+       }
+       /* NOTREACHED */
+}