home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / sys / netinet / ip_icmp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-08  |  13.5 KB  |  520 lines

  1. /*
  2.  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  *
  33.  *    @(#)ip_icmp.c    7.15 (Berkeley) 4/20/91
  34.  */
  35.  
  36. #include "param.h"
  37. #include "systm.h"
  38. #include "malloc.h"
  39. #include "mbuf.h"
  40. #include "protosw.h"
  41. #include "socket.h"
  42. #include "time.h"
  43. #include "kernel.h"
  44.  
  45. #include "../net/route.h"
  46. #include "../net/if.h"
  47.  
  48. #include "in.h"
  49. #include "in_systm.h"
  50. #include "in_var.h"
  51. #include "ip.h"
  52. #include "ip_icmp.h"
  53. #include "icmp_var.h"
  54.  
  55. /*
  56.  * ICMP routines: error generation, receive packet processing, and
  57.  * routines to turnaround packets back to the originator, and
  58.  * host table maintenance routines.
  59.  */
  60. #ifdef ICMPPRINTFS
  61. int    icmpprintfs = 0;
  62. #endif
  63.  
  64. extern    struct protosw inetsw[];
  65.  
  66. /*
  67.  * Generate an error packet of type error
  68.  * in response to bad packet ip.
  69.  */
  70. /*VARARGS3*/
  71. icmp_error(n, type, code, dest)
  72.     struct mbuf *n;
  73.     int type, code;
  74.     struct in_addr dest;
  75. {
  76.     register struct ip *oip = mtod(n, struct ip *), *nip;
  77.     register unsigned oiplen = oip->ip_hl << 2;
  78.     register struct icmp *icp;
  79.     register struct mbuf *m;
  80.     unsigned icmplen;
  81.  
  82. #ifdef ICMPPRINTFS
  83.     if (icmpprintfs)
  84.         printf("icmp_error(%x, %d, %d)\n", oip, type, code);
  85. #endif
  86.     if (type != ICMP_REDIRECT)
  87.         icmpstat.icps_error++;
  88.     /*
  89.      * Don't send error if not the first fragment of message.
  90.      * Don't error if the old packet protocol was ICMP
  91.      * error message, only known informational types.
  92.      */
  93.     if (oip->ip_off &~ (IP_MF|IP_DF))
  94.         goto freeit;
  95.     if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
  96.       n->m_len >= oiplen + ICMP_MINLEN &&
  97.       !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
  98.         icmpstat.icps_oldicmp++;
  99.         goto freeit;
  100.     }
  101.  
  102.     /*
  103.      * First, formulate icmp message
  104.      */
  105.     m = m_gethdr(M_DONTWAIT, MT_HEADER);
  106.     if (m == NULL)
  107.         goto freeit;
  108.     icmplen = oiplen + min(8, oip->ip_len);
  109.     m->m_len = icmplen + ICMP_MINLEN;
  110.     MH_ALIGN(m, m->m_len);
  111.     icp = mtod(m, struct icmp *);
  112.     if ((u_int)type > ICMP_MAXTYPE)
  113.         panic("icmp_error");
  114.     icmpstat.icps_outhist[type]++;
  115.     icp->icmp_type = type;
  116.     if (type == ICMP_REDIRECT)
  117.         icp->icmp_gwaddr = dest;
  118.     else
  119.         icp->icmp_void = 0;
  120.     if (type == ICMP_PARAMPROB) {
  121.         icp->icmp_pptr = code;
  122.         code = 0;
  123.     }
  124.     icp->icmp_code = code;
  125.     bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
  126.     nip = &icp->icmp_ip;
  127.     nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
  128.  
  129.     /*
  130.      * Now, copy old ip header (without options)
  131.      * in front of icmp message.
  132.      */
  133.     if (m->m_data - sizeof(struct ip) < m->m_pktdat)
  134.         panic("icmp len");
  135.     m->m_data -= sizeof(struct ip);
  136.     m->m_len += sizeof(struct ip);
  137.     m->m_pkthdr.len = m->m_len;
  138.     m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
  139.     nip = mtod(m, struct ip *);
  140.     bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
  141.     nip->ip_len = m->m_len;
  142.     nip->ip_hl = sizeof(struct ip) >> 2;
  143.     nip->ip_p = IPPROTO_ICMP;
  144.     icmp_reflect(m);
  145.  
  146. freeit:
  147.     m_freem(n);
  148. }
  149.  
  150. static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
  151. static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
  152. static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
  153. static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
  154. struct sockaddr_in icmpmask = { 8, 0 };
  155. struct in_ifaddr *ifptoia();
  156.  
  157. /*
  158.  * Process a received ICMP message.
  159.  */
  160. icmp_input(m, hlen)
  161.     register struct mbuf *m;
  162.     int hlen;
  163. {
  164.     register struct icmp *icp;
  165.     register struct ip *ip = mtod(m, struct ip *);
  166.     int icmplen = ip->ip_len;
  167.     register int i;
  168.     struct in_ifaddr *ia;
  169.     int (*ctlfunc)(), code;
  170.     extern u_char ip_protox[];
  171.     extern struct in_addr in_makeaddr();
  172.  
  173.     /*
  174.      * Locate icmp structure in mbuf, and check
  175.      * that not corrupted and of at least minimum length.
  176.      */
  177. #ifdef ICMPPRINTFS
  178.     if (icmpprintfs)
  179.         printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
  180. #endif
  181.     if (icmplen < ICMP_MINLEN) {
  182.         icmpstat.icps_tooshort++;
  183.         goto freeit;
  184.     }
  185.     i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
  186.      if (m->m_len < i && (m = m_pullup(m, i)) == 0)  {
  187.         icmpstat.icps_tooshort++;
  188.         return;
  189.     }
  190.      ip = mtod(m, struct ip *);
  191.     m->m_len -= hlen;
  192.     m->m_data += hlen;
  193.     icp = mtod(m, struct icmp *);
  194.     if (in_cksum(m, icmplen)) {
  195.         icmpstat.icps_checksum++;
  196.         goto freeit;
  197.     }
  198.     m->m_len += hlen;
  199.     m->m_data -= hlen;
  200.  
  201. #ifdef ICMPPRINTFS
  202.     /*
  203.      * Message type specific processing.
  204.      */
  205.     if (icmpprintfs)
  206.         printf("icmp_input, type %d code %d\n", icp->icmp_type,
  207.             icp->icmp_code);
  208. #endif
  209.     if (icp->icmp_type > ICMP_MAXTYPE)
  210.         goto raw;
  211.     icmpstat.icps_inhist[icp->icmp_type]++;
  212.     code = icp->icmp_code;
  213.     switch (icp->icmp_type) {
  214.  
  215.     case ICMP_UNREACH:
  216.         if (code > 5)
  217.             goto badcode;
  218.         code += PRC_UNREACH_NET;
  219.         goto deliver;
  220.  
  221.     case ICMP_TIMXCEED:
  222.         if (code > 1)
  223.             goto badcode;
  224.         code += PRC_TIMXCEED_INTRANS;
  225.         goto deliver;
  226.  
  227.     case ICMP_PARAMPROB:
  228.         if (code)
  229.             goto badcode;
  230.         code = PRC_PARAMPROB;
  231.         goto deliver;
  232.  
  233.     case ICMP_SOURCEQUENCH:
  234.         if (code)
  235.             goto badcode;
  236.         code = PRC_QUENCH;
  237.     deliver:
  238.         /*
  239.          * Problem with datagram; advise higher level routines.
  240.          */
  241.         if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
  242.             icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
  243.             icmpstat.icps_badlen++;
  244.             goto freeit;
  245.         }
  246.         NTOHS(icp->icmp_ip.ip_len);
  247. #ifdef ICMPPRINTFS
  248.         if (icmpprintfs)
  249.             printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
  250. #endif
  251.         icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
  252.         if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
  253.             (*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
  254.                 (caddr_t) &icp->icmp_ip);
  255.         break;
  256.  
  257.     badcode:
  258.         icmpstat.icps_badcode++;
  259.         break;
  260.  
  261.     case ICMP_ECHO:
  262.         icp->icmp_type = ICMP_ECHOREPLY;
  263.         goto reflect;
  264.  
  265.     case ICMP_TSTAMP:
  266.         if (icmplen < ICMP_TSLEN) {
  267.             icmpstat.icps_badlen++;
  268.             break;
  269.         }
  270.         icp->icmp_type = ICMP_TSTAMPREPLY;
  271.         icp->icmp_rtime = iptime();
  272.         icp->icmp_ttime = icp->icmp_rtime;    /* bogus, do later! */
  273.         goto reflect;
  274.         
  275.     case ICMP_IREQ:
  276. #define    satosin(sa)    ((struct sockaddr_in *)(sa))
  277.         if (in_netof(ip->ip_src) == 0 &&
  278.             (ia = ifptoia(m->m_pkthdr.rcvif)))
  279.             ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
  280.                 in_lnaof(ip->ip_src));
  281.         icp->icmp_type = ICMP_IREQREPLY;
  282.         goto reflect;
  283.  
  284.     case ICMP_MASKREQ:
  285.         if (icmplen < ICMP_MASKLEN ||
  286.             (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
  287.             break;
  288.         icp->icmp_type = ICMP_MASKREPLY;
  289.         icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
  290.         if (ip->ip_src.s_addr == 0) {
  291.             if (ia->ia_ifp->if_flags & IFF_BROADCAST)
  292.                 ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
  293.             else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
  294.                 ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
  295.         }
  296. reflect:
  297.         ip->ip_len += hlen;    /* since ip_input deducts this */
  298.         icmpstat.icps_reflect++;
  299.         icmpstat.icps_outhist[icp->icmp_type]++;
  300.         icmp_reflect(m);
  301.         return;
  302.  
  303.     case ICMP_REDIRECT:
  304.         if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
  305.             icmpstat.icps_badlen++;
  306.             break;
  307.         }
  308.         /*
  309.          * Short circuit routing redirects to force
  310.          * immediate change in the kernel's routing
  311.          * tables.  The message is also handed to anyone
  312.          * listening on a raw socket (e.g. the routing
  313.          * daemon for use in updating its tables).
  314.          */
  315.         icmpgw.sin_addr = ip->ip_src;
  316.         icmpdst.sin_addr = icp->icmp_gwaddr;
  317. #ifdef    ICMPPRINTFS
  318.         if (icmpprintfs)
  319.             printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
  320.                 icp->icmp_gwaddr);
  321. #endif
  322.         if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
  323.             u_long in_netof();
  324.             icmpsrc.sin_addr =
  325.              in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
  326.             in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask);
  327.             rtredirect((struct sockaddr *)&icmpsrc,
  328.               (struct sockaddr *)&icmpdst,
  329.               (struct sockaddr *)&icmpmask, RTF_GATEWAY,
  330.               (struct sockaddr *)&icmpgw, (struct rtentry **)0);
  331.             icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
  332.             pfctlinput(PRC_REDIRECT_NET,
  333.               (struct sockaddr *)&icmpsrc);
  334.         } else {
  335.             icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
  336.             rtredirect((struct sockaddr *)&icmpsrc,
  337.               (struct sockaddr *)&icmpdst,
  338.               (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
  339.               (struct sockaddr *)&icmpgw, (struct rtentry **)0);
  340.             pfctlinput(PRC_REDIRECT_HOST,
  341.               (struct sockaddr *)&icmpsrc);
  342.         }
  343.         break;
  344.  
  345.     /*
  346.      * No kernel processing for the following;
  347.      * just fall through to send to raw listener.
  348.      */
  349.     case ICMP_ECHOREPLY:
  350.     case ICMP_TSTAMPREPLY:
  351.     case ICMP_IREQREPLY:
  352.     case ICMP_MASKREPLY:
  353.     default:
  354.         break;
  355.     }
  356.  
  357. raw:
  358.     icmpsrc.sin_addr = ip->ip_src;
  359.     icmpdst.sin_addr = ip->ip_dst;
  360.     (void) raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
  361.         (struct sockaddr *)&icmpdst);
  362.     return;
  363.  
  364. freeit:
  365.     m_freem(m);
  366. }
  367.  
  368. /*
  369.  * Reflect the ip packet back to the source
  370.  */
  371. icmp_reflect(m)
  372.     struct mbuf *m;
  373. {
  374.     register struct ip *ip = mtod(m, struct ip *);
  375.     register struct in_ifaddr *ia;
  376.     struct in_addr t;
  377.     struct mbuf *opts = 0, *ip_srcroute();
  378.     int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
  379.  
  380.     t = ip->ip_dst;
  381.     ip->ip_dst = ip->ip_src;
  382.     /*
  383.      * If the incoming packet was addressed directly to us,
  384.      * use dst as the src for the reply.  Otherwise (broadcast
  385.      * or anonymous), use the address which corresponds
  386.      * to the incoming interface.
  387.      */
  388.     for (ia = in_ifaddr; ia; ia = ia->ia_next) {
  389.         if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
  390.             break;
  391.         if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
  392.             t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
  393.             break;
  394.     }
  395.     if (ia == (struct in_ifaddr *)0)
  396.         ia = ifptoia(m->m_pkthdr.rcvif);
  397.     if (ia == (struct in_ifaddr *)0)
  398.         ia = in_ifaddr;
  399.     t = IA_SIN(ia)->sin_addr;
  400.     ip->ip_src = t;
  401.     ip->ip_ttl = MAXTTL;
  402.  
  403.     if (optlen > 0) {
  404.         register u_char *cp;
  405.         int opt, cnt;
  406.         u_int len;
  407.  
  408.         /*
  409.          * Retrieve any source routing from the incoming packet;
  410.          * add on any record-route or timestamp options.
  411.          */
  412.         cp = (u_char *) (ip + 1);
  413.         if ((opts = ip_srcroute()) == 0 &&
  414.             (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
  415.             opts->m_len = sizeof(struct in_addr);
  416.             mtod(opts, struct in_addr *)->s_addr = 0;
  417.         }
  418.         if (opts) {
  419. #ifdef ICMPPRINTFS
  420.             if (icmpprintfs)
  421.                 printf("icmp_reflect optlen %d rt %d => ",
  422.                 optlen, opts->m_len);
  423. #endif
  424.             for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
  425.                 opt = cp[IPOPT_OPTVAL];
  426.                 if (opt == IPOPT_EOL)
  427.                     break;
  428.                 if (opt == IPOPT_NOP)
  429.                     len = 1;
  430.                 else {
  431.                     len = cp[IPOPT_OLEN];
  432.                     if (len <= 0 || len > cnt)
  433.                         break;
  434.                 }
  435.                 /*
  436.                  * should check for overflow, but it "can't happen"
  437.                  */
  438.                 if (opt == IPOPT_RR || opt == IPOPT_TS) {
  439.                     bcopy((caddr_t)cp,
  440.                     mtod(opts, caddr_t) + opts->m_len, len);
  441.                     opts->m_len += len;
  442.                 }
  443.             }
  444.             if (opts->m_len % 4 != 0) {
  445.                 *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL;
  446.                 opts->m_len++;
  447.             }
  448. #ifdef ICMPPRINTFS
  449.             if (icmpprintfs)
  450.                 printf("%d\n", opts->m_len);
  451. #endif
  452.         }
  453.         /*
  454.          * Now strip out original options by copying rest of first
  455.          * mbuf's data back, and adjust the IP length.
  456.          */
  457.         ip->ip_len -= optlen;
  458.         ip->ip_hl = sizeof(struct ip) >> 2;
  459.         m->m_len -= optlen;
  460.         if (m->m_flags & M_PKTHDR)
  461.             m->m_pkthdr.len -= optlen;
  462.         optlen += sizeof(struct ip);
  463.         bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
  464.              (unsigned)(m->m_len - sizeof(struct ip)));
  465.     }
  466.     icmp_send(m, opts);
  467.     if (opts)
  468.         (void)m_free(opts);
  469. }
  470.  
  471. struct in_ifaddr *
  472. ifptoia(ifp)
  473.     struct ifnet *ifp;
  474. {
  475.     register struct in_ifaddr *ia;
  476.  
  477.     for (ia = in_ifaddr; ia; ia = ia->ia_next)
  478.         if (ia->ia_ifp == ifp)
  479.             return (ia);
  480.     return ((struct in_ifaddr *)0);
  481. }
  482.  
  483. /*
  484.  * Send an icmp packet back to the ip level,
  485.  * after supplying a checksum.
  486.  */
  487. icmp_send(m, opts)
  488.     register struct mbuf *m;
  489.     struct mbuf *opts;
  490. {
  491.     register struct ip *ip = mtod(m, struct ip *);
  492.     register int hlen;
  493.     register struct icmp *icp;
  494.  
  495.     hlen = ip->ip_hl << 2;
  496.     m->m_data += hlen;
  497.     m->m_len -= hlen;
  498.     icp = mtod(m, struct icmp *);
  499.     icp->icmp_cksum = 0;
  500.     icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
  501.     m->m_data -= hlen;
  502.     m->m_len += hlen;
  503. #ifdef ICMPPRINTFS
  504.     if (icmpprintfs)
  505.         printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
  506. #endif
  507.     (void) ip_output(m, opts, (struct route *)0, 0);
  508. }
  509.  
  510. n_time
  511. iptime()
  512. {
  513.     struct timeval atv;
  514.     u_long t;
  515.  
  516.     microtime(&atv);
  517.     t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
  518.     return (htonl(t));
  519. }
  520.