4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / sys / netinet / ip_icmp.c
index 9be9c3f..33f4c25 100644 (file)
@@ -1,49 +1,54 @@
 /*
 /*
- * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)ip_icmp.c   7.14 (Berkeley) %G%
+ *     @(#)ip_icmp.c   8.1 (Berkeley) %G%
  */
 
  */
 
-#include "param.h"
-#include "systm.h"
-#include "malloc.h"
-#include "mbuf.h"
-#include "protosw.h"
-#include "socket.h"
-#include "time.h"
-#include "kernel.h"
-
-#include "../net/route.h"
-#include "../net/if.h"
-
-#include "in.h"
-#include "in_systm.h"
-#include "in_var.h"
-#include "ip.h"
-#include "ip_icmp.h"
-#include "icmp_var.h"
+#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    icmpmaskrepl = 0;
 #ifdef ICMPPRINTFS
 int    icmpprintfs = 0;
 #endif
 
 #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.
  */
-/*VARARGS3*/
-icmp_error(n, type, code, dest)
+void
+icmp_error(n, type, code, dest, destifp)
        struct mbuf *n;
        int type, code;
        struct mbuf *n;
        int type, code;
-       struct in_addr dest;
+       n_long dest;
+       struct ifnet *destifp;
 {
        register struct ip *oip = mtod(n, struct ip *), *nip;
        register unsigned oiplen = oip->ip_hl << 2;
 {
        register struct ip *oip = mtod(n, struct ip *), *nip;
        register unsigned oiplen = oip->ip_hl << 2;
@@ -71,7 +76,9 @@ icmp_error(n, type, code, dest)
                icmpstat.icps_oldicmp++;
                goto freeit;
        }
                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
         */
@@ -87,13 +94,22 @@ icmp_error(n, type, code, dest)
        icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
        if (type == ICMP_REDIRECT)
        icmpstat.icps_outhist[type]++;
        icp->icmp_type = type;
        if (type == ICMP_REDIRECT)
-               icp->icmp_gwaddr = dest;
-       else
+               icp->icmp_gwaddr.s_addr = dest;
+       else {
                icp->icmp_void = 0;
                icp->icmp_void = 0;
-       if (type == ICMP_PARAMPROB) {
-               icp->icmp_pptr = code;
-               code = 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;
        bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
        nip = &icp->icmp_ip;
        icp->icmp_code = code;
        bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
        nip = &icp->icmp_ip;
@@ -110,11 +126,12 @@ icmp_error(n, type, code, dest)
        m->m_pkthdr.len = m->m_len;
        m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
        nip = mtod(m, struct ip *);
        m->m_pkthdr.len = m->m_len;
        m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
        nip = mtod(m, struct ip *);
-       bcopy((caddr_t)oip, (caddr_t)nip, 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_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_tos = 0;
        icmp_reflect(m);
 
 freeit:
        icmp_reflect(m);
 
 freeit:
@@ -126,11 +143,11 @@ 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 };
 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 };
-struct in_ifaddr *ifptoia();
 
 /*
  * Process a received ICMP message.
  */
 
 /*
  * Process a received ICMP message.
  */
+void
 icmp_input(m, hlen)
        register struct mbuf *m;
        int hlen;
 icmp_input(m, hlen)
        register struct mbuf *m;
        int hlen;
@@ -140,9 +157,9 @@ icmp_input(m, hlen)
        int icmplen = ip->ip_len;
        register int i;
        struct in_ifaddr *ia;
        int icmplen = ip->ip_len;
        register int i;
        struct in_ifaddr *ia;
-       int (*ctlfunc)(), code;
+       void (*ctlfunc) __P((int, struct sockaddr *, struct ip *));
+       int code;
        extern u_char ip_protox[];
        extern u_char ip_protox[];
-       extern struct in_addr in_makeaddr();
 
        /*
         * Locate icmp structure in mbuf, and check
 
        /*
         * Locate icmp structure in mbuf, and check
@@ -150,18 +167,20 @@ icmp_input(m, hlen)
         */
 #ifdef ICMPPRINTFS
        if (icmpprintfs)
         */
 #ifdef ICMPPRINTFS
        if (icmpprintfs)
-               printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
+               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;
        }
 #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)  {
+       i = hlen + min(icmplen, ICMP_ADVLENMIN);
+       if (m->m_len < i && (m = m_pullup(m, i)) == 0)  {
                icmpstat.icps_tooshort++;
                return;
        }
                icmpstat.icps_tooshort++;
                return;
        }
-       ip = mtod(m, struct ip *);
+       ip = mtod(m, struct ip *);
        m->m_len -= hlen;
        m->m_data += hlen;
        icp = mtod(m, struct icmp *);
        m->m_len -= hlen;
        m->m_data += hlen;
        icp = mtod(m, struct icmp *);
@@ -187,9 +206,35 @@ icmp_input(m, hlen)
        switch (icp->icmp_type) {
 
        case ICMP_UNREACH:
        switch (icp->icmp_type) {
 
        case ICMP_UNREACH:
-               if (code > 5)
-                       goto badcode;
-               code += PRC_UNREACH_NET;
+               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:
                goto deliver;
 
        case ICMP_TIMXCEED:
@@ -199,7 +244,7 @@ icmp_input(m, hlen)
                goto deliver;
 
        case ICMP_PARAMPROB:
                goto deliver;
 
        case ICMP_PARAMPROB:
-               if (code)
+               if (code > 1)
                        goto badcode;
                code = PRC_PARAMPROB;
                goto deliver;
                        goto badcode;
                code = PRC_PARAMPROB;
                goto deliver;
@@ -225,7 +270,7 @@ icmp_input(m, hlen)
                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,
                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,
-                           (caddr_t) &icp->icmp_ip);
+                           &icp->icmp_ip);
                break;
 
        badcode:
                break;
 
        badcode:
@@ -246,18 +291,29 @@ icmp_input(m, hlen)
                icp->icmp_ttime = icp->icmp_rtime;      /* bogus, do later! */
                goto reflect;
                
                icp->icmp_ttime = icp->icmp_rtime;      /* bogus, do later! */
                goto reflect;
                
-       case ICMP_IREQ:
+       case ICMP_MASKREQ:
 #define        satosin(sa)     ((struct sockaddr_in *)(sa))
 #define        satosin(sa)     ((struct sockaddr_in *)(sa))
-               if (in_netof(ip->ip_src) == 0 &&
-                   (ia = ifptoia(m->m_pkthdr.rcvif)))
-                       ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
-                           in_lnaof(ip->ip_src));
-               icp->icmp_type = ICMP_IREQREPLY;
-               goto reflect;
+               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 ICMP_MASKREQ:
-               if (icmplen < ICMP_MASKLEN ||
-                   (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
+               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;
                        break;
                icp->icmp_type = ICMP_MASKREPLY;
                icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
@@ -275,7 +331,10 @@ reflect:
                return;
 
        case ICMP_REDIRECT:
                return;
 
        case ICMP_REDIRECT:
-               if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
+               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;
                }
                        icmpstat.icps_badlen++;
                        break;
                }
@@ -293,27 +352,12 @@ reflect:
                        printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
                                icp->icmp_gwaddr);
 #endif
                        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) {
-                       u_long in_netof();
-                       icmpsrc.sin_addr =
-                        in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
-                       in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask);
-                       rtredirect((struct sockaddr *)&icmpsrc,
-                         (struct sockaddr *)&icmpdst,
-                         (struct sockaddr *)&icmpmask, RTF_GATEWAY,
-                         (struct sockaddr *)&icmpgw, (struct rtentry **)0);
-                       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,
-                         (struct sockaddr *)&icmpdst,
-                         (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
-                         (struct sockaddr *)&icmpgw, (struct rtentry **)0);
-                       pfctlinput(PRC_REDIRECT_HOST,
-                         (struct sockaddr *)&icmpsrc);
-               }
+               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;
 
        /*
                break;
 
        /*
@@ -321,6 +365,8 @@ reflect:
         * just fall through to send to raw listener.
         */
        case ICMP_ECHOREPLY:
         * just fall through to send to raw listener.
         */
        case ICMP_ECHOREPLY:
+       case ICMP_ROUTERADVERT:
+       case ICMP_ROUTERSOLICIT:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
        case ICMP_MASKREPLY:
        case ICMP_TSTAMPREPLY:
        case ICMP_IREQREPLY:
        case ICMP_MASKREPLY:
@@ -329,10 +375,7 @@ reflect:
        }
 
 raw:
        }
 
 raw:
-       icmpsrc.sin_addr = ip->ip_src;
-       icmpdst.sin_addr = ip->ip_dst;
-       (void) raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
-           (struct sockaddr *)&icmpdst);
+       rip_input(m);
        return;
 
 freeit:
        return;
 
 freeit:
@@ -342,6 +385,7 @@ freeit:
 /*
  * Reflect the ip packet back to the source
  */
 /*
  * Reflect the ip packet back to the source
  */
+void
 icmp_reflect(m)
        struct mbuf *m;
 {
 icmp_reflect(m)
        struct mbuf *m;
 {
@@ -351,6 +395,12 @@ icmp_reflect(m)
        struct mbuf *opts = 0, *ip_srcroute();
        int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
 
        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;
        /*
@@ -366,8 +416,14 @@ icmp_reflect(m)
                    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
                        break;
        }
                    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
                        break;
        }
+       icmpdst.sin_addr = t;
        if (ia == (struct in_ifaddr *)0)
        if (ia == (struct in_ifaddr *)0)
-               ia = ifptoia(m->m_pkthdr.rcvif);
+               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;
        if (ia == (struct in_ifaddr *)0)
                ia = in_ifaddr;
        t = IA_SIN(ia)->sin_addr;
@@ -412,17 +468,22 @@ icmp_reflect(m)
                                            break;
                            }
                            /*
                                            break;
                            }
                            /*
-                            * should check for overflow, but it "can't happen"
+                            * Should check for overflow, but it "can't happen"
                             */
                             */
-                           if (opt == IPOPT_RR || opt == IPOPT_TS) {
+                           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;
                            }
                    }
                                    bcopy((caddr_t)cp,
                                        mtod(opts, caddr_t) + opts->m_len, len);
                                    opts->m_len += len;
                            }
                    }
-                   if (opts->m_len % 4 != 0) {
-                           *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL;
-                           opts->m_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)
                    }
 #ifdef ICMPPRINTFS
                    if (icmpprintfs)
@@ -442,28 +503,19 @@ icmp_reflect(m)
                bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
                         (unsigned)(m->m_len - 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);
 
        icmp_send(m, opts);
+done:
        if (opts)
                (void)m_free(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.
  */
+void
 icmp_send(m, opts)
        register struct mbuf *m;
        struct mbuf *opts;
 icmp_send(m, opts)
        register struct mbuf *m;
        struct mbuf *opts;
@@ -484,7 +536,7 @@ icmp_send(m, opts)
        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, opts, (struct route *)0, 0);
+       (void) ip_output(m, opts, NULL, 0, NULL);
 }
 
 n_time
 }
 
 n_time
@@ -497,3 +549,27 @@ iptime()
        t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
        return (htonl(t));
 }
        t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
        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 */
+}