new stats; change keepalives to use rcv_nxt instead of rcv_nxt-1
[unix-history] / usr / src / sys / netinet / ip_icmp.c
index 285af5a..2167ded 100644 (file)
@@ -1,4 +1,10 @@
-/*     ip_icmp.c       6.11    85/05/27        */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)ip_icmp.c   7.2 (Berkeley) %G%
+ */
 
 #include "param.h"
 #include "systm.h"
 
 #include "param.h"
 #include "systm.h"
@@ -31,30 +37,33 @@ int icmpprintfs = 0;
  * 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)
+/*VARARGS4*/
+icmp_error(oip, type, code, ifp, dest)
        struct ip *oip;
        int type, code;
        struct ip *oip;
        int type, code;
+       struct ifnet *ifp;
+       struct in_addr dest;
 {
        register unsigned oiplen = oip->ip_hl << 2;
        register struct icmp *icp;
        struct mbuf *m;
        struct ip *nip;
 {
        register unsigned oiplen = oip->ip_hl << 2;
        register struct icmp *icp;
        struct mbuf *m;
        struct ip *nip;
+       unsigned icmplen;
 
 #ifdef ICMPPRINTFS
        if (icmpprintfs)
                printf("icmp_error(%x, %d, %d)\n", oip, type, code);
 #endif
 
 #ifdef ICMPPRINTFS
        if (icmpprintfs)
                printf("icmp_error(%x, %d, %d)\n", oip, type, code);
 #endif
-       icmpstat.icps_error++;
+       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 EVER error if the old packet protocol was ICMP.
+        * (Could do ECHO, etc, but not error indications.)
         */
         */
-       if (oip->ip_len < 8) {
-               icmpstat.icps_oldshort++;
+       if (oip->ip_off &~ (IP_MF|IP_DF))
                goto free;
                goto free;
-       }
-       if (oip->ip_p == IPPROTO_ICMP) {
+       if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) {
                icmpstat.icps_oldicmp++;
                goto free;
        }
                icmpstat.icps_oldicmp++;
                goto free;
        }
@@ -65,37 +74,42 @@ icmp_error(oip, type, code)
        m = m_get(M_DONTWAIT, MT_HEADER);
        if (m == NULL)
                goto free;
        m = m_get(M_DONTWAIT, MT_HEADER);
        if (m == NULL)
                goto free;
-       m->m_len = oiplen + 8 + ICMP_MINLEN;
+       icmplen = oiplen + MIN(8, oip->ip_len);
+       m->m_len = icmplen + ICMP_MINLEN;
        m->m_off = MMAXOFF - m->m_len;
        icp = mtod(m, struct icmp *);
        m->m_off = MMAXOFF - m->m_len;
        icp = mtod(m, struct icmp *);
-       if ((u_int)type > ICMP_IREQREPLY)
+       if ((u_int)type > ICMP_MAXTYPE)
                panic("icmp_error");
        icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
                panic("icmp_error");
        icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
-       icp->icmp_void = 0;
+       if (type == ICMP_REDIRECT)
+               icp->icmp_gwaddr = dest;
+       else
+               icp->icmp_void = 0;
        if (type == ICMP_PARAMPROB) {
                icp->icmp_pptr = code;
                code = 0;
        }
        icp->icmp_code = code;
        if (type == ICMP_PARAMPROB) {
                icp->icmp_pptr = code;
                code = 0;
        }
        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->ip_len += oiplen;
        nip->ip_len = htons((u_short)nip->ip_len);
 
        /*
        nip = &icp->icmp_ip;
        nip->ip_len += oiplen;
        nip->ip_len = htons((u_short)nip->ip_len);
 
        /*
-        * 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 in front of icmp message.
         */
         */
+       if (m->m_len + oiplen > MLEN)
+               oiplen = sizeof(struct ip);
+       if (m->m_len + oiplen > MLEN)
+               panic("icmp len");
        m->m_off -= oiplen;
        m->m_off -= oiplen;
+       m->m_len += oiplen;
        nip = mtod(m, struct ip *);
        bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
        nip = mtod(m, struct ip *);
        bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
-       nip->ip_len = m->m_len + oiplen;
+       nip->ip_len = m->m_len;
        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);
+       icmp_reflect(nip, ifp);
 
 free:
        m_freem(dtom(oip));
 
 free:
        m_freem(dtom(oip));
@@ -104,17 +118,21 @@ free:
 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
 static struct sockaddr_in icmpsrc = { AF_INET };
 static struct sockaddr_in icmpdst = { AF_INET };
 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 icmpgw = { AF_INET };
+struct in_ifaddr *ifptoia();
 
 /*
  * Process a received ICMP message.
  */
 
 /*
  * Process a received ICMP message.
  */
-icmp_input(m)
-       struct mbuf *m;
+icmp_input(m, ifp)
+       register struct mbuf *m;
+       struct ifnet *ifp;
 {
        register struct icmp *icp;
        register struct ip *ip = mtod(m, struct ip *);
        int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
        register int i;
 {
        register struct icmp *icp;
        register struct ip *ip = mtod(m, struct ip *);
        int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
        register int i;
+       struct in_ifaddr *ia;
        int (*ctlfunc)(), code;
        extern u_char ip_protox[];
        extern struct in_addr in_makeaddr();
        int (*ctlfunc)(), code;
        extern u_char ip_protox[];
        extern struct in_addr in_makeaddr();
@@ -131,8 +149,7 @@ icmp_input(m)
                icmpstat.icps_tooshort++;
                goto free;
        }
                icmpstat.icps_tooshort++;
                goto free;
        }
-       /* THIS LENGTH CHECK STILL MISSES ANY IP OPTIONS IN ICMP_IP */
-       i = MIN(icmplen, ICMP_ADVLENMIN + hlen);
+       i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
        if ((m->m_off > MMAXOFF || m->m_len < i) &&
                (m = m_pullup(m, i)) == 0)  {
                icmpstat.icps_tooshort++;
        if ((m->m_off > MMAXOFF || m->m_len < i) &&
                (m = m_pullup(m, i)) == 0)  {
                icmpstat.icps_tooshort++;
@@ -146,6 +163,8 @@ icmp_input(m)
                icmpstat.icps_checksum++;
                goto free;
        }
                icmpstat.icps_checksum++;
                goto free;
        }
+       m->m_len += hlen;
+       m->m_off -= hlen;
 
 #ifdef ICMPPRINTFS
        /*
 
 #ifdef ICMPPRINTFS
        /*
@@ -155,8 +174,8 @@ icmp_input(m)
                printf("icmp_input, type %d code %d\n", icp->icmp_type,
                    icp->icmp_code);
 #endif
                printf("icmp_input, type %d code %d\n", icp->icmp_type,
                    icp->icmp_code);
 #endif
-       if (icp->icmp_type > ICMP_IREQREPLY)
-               goto free;
+       if (icp->icmp_type > ICMP_MAXTYPE)
+               goto raw;
        icmpstat.icps_inhist[icp->icmp_type]++;
        code = icp->icmp_code;
        switch (icp->icmp_type) {
        icmpstat.icps_inhist[icp->icmp_type]++;
        code = icp->icmp_code;
        switch (icp->icmp_type) {
@@ -196,13 +215,14 @@ icmp_input(m)
                if (icmpprintfs)
                        printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
 #endif
                if (icmpprintfs)
                        printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
 #endif
+               icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
                if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
                if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
-                       (*ctlfunc)(code, (caddr_t)icp);
-               goto free;
+                       (*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
+               break;
 
        badcode:
                icmpstat.icps_badcode++;
 
        badcode:
                icmpstat.icps_badcode++;
-               goto free;
+               break;
 
        case ICMP_ECHO:
                icp->icmp_type = ICMP_ECHOREPLY;
 
        case ICMP_ECHO:
                icp->icmp_type = ICMP_ECHOREPLY;
@@ -211,7 +231,7 @@ icmp_input(m)
        case ICMP_TSTAMP:
                if (icmplen < ICMP_TSLEN) {
                        icmpstat.icps_badlen++;
        case ICMP_TSTAMP:
                if (icmplen < ICMP_TSLEN) {
                        icmpstat.icps_badlen++;
-                       goto free;
+                       break;
                }
                icp->icmp_type = ICMP_TSTAMPREPLY;
                icp->icmp_rtime = iptime();
                }
                icp->icmp_type = ICMP_TSTAMPREPLY;
                icp->icmp_rtime = iptime();
@@ -220,17 +240,34 @@ icmp_input(m)
                
        case ICMP_IREQ:
 #define        satosin(sa)     ((struct sockaddr_in *)(sa))
                
        case ICMP_IREQ:
 #define        satosin(sa)     ((struct sockaddr_in *)(sa))
-               if (in_netof(ip->ip_src) == 0)
-                       ip->ip_src = in_makeaddr(
-                           in_netof(satosin(&in_ifaddr->ia_addr)->sin_addr),
+               if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp)))
+                       ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
                            in_lnaof(ip->ip_src));
                icp->icmp_type = ICMP_IREQREPLY;
                goto reflect;
 
                            in_lnaof(ip->ip_src));
                icp->icmp_type = ICMP_IREQREPLY;
                goto reflect;
 
+       case ICMP_MASKREQ:
+               if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0)
+                       break;
+               icp->icmp_type = ICMP_MASKREPLY;
+               icp->icmp_mask = ia->ia_netmask;
+               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(ip, ifp);
+               return;
+
        case ICMP_REDIRECT:
                if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
                        icmpstat.icps_badlen++;
        case ICMP_REDIRECT:
                if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
                        icmpstat.icps_badlen++;
-                       goto free;
+                       break;
                }
                /*
                 * Short circuit routing redirects to force
                }
                /*
                 * Short circuit routing redirects to force
@@ -239,73 +276,122 @@ icmp_input(m)
                 * listening on a raw socket (e.g. the routing
                 * daemon for use in updating its tables).
                 */
                 * 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;
                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
                if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
                        icmpsrc.sin_addr =
                         in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
                        rtredirect((struct sockaddr *)&icmpsrc,
                if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
                        icmpsrc.sin_addr =
                         in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
                        rtredirect((struct sockaddr *)&icmpsrc,
-                         (struct sockaddr *)&icmpdst, RTF_GATEWAY);
-                       pfctlinput(PRC_REDIRECT_NET, (caddr_t)icp);
+                         (struct sockaddr *)&icmpdst, RTF_GATEWAY,
+                         (struct sockaddr *)&icmpgw);
+                       icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+                       pfctlinput(PRC_REDIRECT_NET,
+                         (struct sockaddr *)&icmpsrc);
                } else {
                        icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
                        rtredirect((struct sockaddr *)&icmpsrc,
                } else {
                        icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
                        rtredirect((struct sockaddr *)&icmpsrc,
-                         (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST);
-                       pfctlinput(PRC_REDIRECT_HOST, (caddr_t)icp);
+                         (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST,
+                         (struct sockaddr *)&icmpgw);
+                       pfctlinput(PRC_REDIRECT_HOST,
+                         (struct sockaddr *)&icmpsrc);
                }
                }
-               /* FALL THROUGH */
+               break;
 
 
+       /*
+        * No kernel processing for the following;
+        * just fall through to send to raw listener.
+        */
        case ICMP_ECHOREPLY:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
        case ICMP_ECHOREPLY:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
-               icmpsrc.sin_addr = ip->ip_src;
-               icmpdst.sin_addr = ip->ip_dst;
-               raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
-                 (struct sockaddr *)&icmpdst);
-               return;
-
+       case ICMP_MASKREPLY:
        default:
        default:
-               goto free;
+               break;
        }
        }
-reflect:
-       ip->ip_len += hlen;             /* since ip_input deducts this */
-       icmpstat.icps_reflect++;
-       icmpstat.icps_outhist[icp->icmp_type]++;
-       icmp_reflect(ip);
+
+raw:
+       icmpsrc.sin_addr = ip->ip_src;
+       icmpdst.sin_addr = ip->ip_dst;
+       raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
+           (struct sockaddr *)&icmpdst);
        return;
        return;
+
 free:
 free:
-       m_freem(dtom(ip));
+       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;
+icmp_reflect(ip, ifp)
+       register struct ip *ip;
+       struct ifnet *ifp;
 {
 {
-       register struct in_addr t;
        register struct in_ifaddr *ia;
        register struct in_ifaddr *ia;
+       struct in_addr t;
+       struct mbuf *opts = 0, *ip_srcroute();
+       int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
 
        t = ip->ip_dst;
 
        t = ip->ip_dst;
-       if (t.s_addr == INADDR_ANY)
-               t = IA_SIN(in_ifaddr)->sin_addr;
-       else for (ia = in_ifaddr; ia; ia = ia->ia_next)
-               if (t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr &&
-                   (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
-                       t = IA_SIN(ia)->sin_addr;
-                       break;
-               }
        ip->ip_dst = ip->ip_src;
        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;
+       }
+       if (ia == (struct in_ifaddr *)0)
+               ia = ifptoia(ifp);
+       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);
+
+       if (optlen > 0) {
+               /*
+                * Retrieve any source routing from the incoming packet
+                * and strip out other options.  Adjust the IP length.
+                */
+               opts = ip_srcroute();
+               ip->ip_len -= optlen;
+               ip_stripoptions(ip, (struct mbuf *)0);
+       }
+       icmp_send(ip, opts);
+       if (opts)
+               (void)m_free(opts);
+}
+
+struct in_ifaddr *
+ifptoia(ifp)
+       struct ifnet *ifp;
+{
+       register struct in_ifaddr *ia;
+
+       for (ia = in_ifaddr; ia; ia = ia->ia_next)
+               if (ia->ia_ifp == ifp)
+                       return (ia);
+       return ((struct in_ifaddr *)0);
 }
 
 /*
  * 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;
+icmp_send(ip, opts)
+       register struct ip *ip;
+       struct mbuf *opts;
 {
        register int hlen;
        register struct icmp *icp;
 {
        register int hlen;
        register struct icmp *icp;
@@ -313,6 +399,8 @@ icmp_send(ip)
 
        m = dtom(ip);
        hlen = ip->ip_hl << 2;
 
        m = dtom(ip);
        hlen = ip->ip_hl << 2;
+       m->m_off += 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);
@@ -322,16 +410,16 @@ icmp_send(ip)
        if (icmpprintfs)
                printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
 #endif
        if (icmpprintfs)
                printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
 #endif
-       (void) ip_output(m, (struct mbuf *)0, (struct route *)0, 0);
+       (void) ip_output(m, opts, (struct route *)0, 0);
 }
 
 n_time
 iptime()
 {
 }
 
 n_time
 iptime()
 {
-       int s = spl6();
+       struct timeval atv;
        u_long t;
 
        u_long t;
 
-       t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
-       splx(s);
+       microtime(&atv);
+       t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
        return (htonl(t));
 }
        return (htonl(t));
 }