#!/usr/bin/perl

# traceroute hop spoofer!
# -Samy Kamkar [cp5@LucidX.com]

use Packet::Inject;
use Packet::IP;
use Packet::Ethernet;
use Packet::ICMP;
use Packet::Definitions;
use Packet::Lookup;

use Net::Pcap;
use strict;

die "usage: $0 <spoof 1> <spoof 2> ...\n" unless my @spoofs = @ARGV;

# convert hosts to ip now to save time when sending packets
foreach (@spoofs) {
 $_ = &Packet::Lookup::quad2int(&Packet::Lookup::host2ip($_));
}

my $filter_t;
my $id     = int(rand(2 ** 16));
my $total  = @spoofs;
my $filt   = "ip[8] <= " . ($total + 1);
my $err    = '';
my $device = Net::Pcap::lookupdev(\$err);
my $cap    = Net::Pcap::open_live($device, 2000, 0, 1000, \$err);
my $inject = Packet::Inject->new(device => $device);

# create standard ethernet packet
my $sendeth = Packet::Ethernet->new(
  type     => 0x0800,
);
my $ethernet = Packet::Ethernet->new();
my $ip = Packet::IP->new();
my $ignore;

$inject->open_bpf() || die $inject->{errbuf};
Net::Pcap::compile($cap, \$filter_t, $filt, 1, 0);
Net::Pcap::setfilter($cap, $filter_t);
Net::Pcap::loop($cap, 0, \&parse , $total);

sub parse {
  my ($total, $hdr, $packet) = @_;
  my ($sendip, $sendicmp);

  if ($ignore) {
    $ip->decode(substr($packet, 14));
  }
  else {
    $ignore++;
    $ethernet->decode($packet);
    $sendeth->{dest_mac} = $ethernet->src_mac;
    $sendeth = "$sendeth";
    $ip->decode($ethernet->data);
  }

  # this is where we send the final packet and get device name
  if ($ip->ttl == $total + 1) {
    $sendicmp = Packet::ICMP->new(
      type          => &Packet::ICMP::ICMP_DEST_UNREACH,
      code          => &Packet::ICMP::ICMP_PORT_UNREACH,
      data          => "\0" x 4 . substr($ip->encode, 0, 28),
    );

    $sendip   = Packet::IP->new(
      src_ip        => $ip->dest_ip,
      dest_ip       => $ip->src_ip,
      id            => $id++,
      proto         => 1,
      data          => $sendicmp,
    );

    $inject->write_bpf(packet => $sendeth . $sendip);

    print "Port Unreachable sent to " . &Packet::Lookup::int2quad($ip->src_ip) . "\n";
  }

  else {
    $sendicmp = Packet::ICMP->new(
      type => &Packet::ICMP::ICMP_TIME_EXCEED,
      code => &Packet::ICMP::ICMP_TTL_EXCEED,
      data => "\0" x 4 . substr($ip->encode, 0, 28),
    );
    $sendip   = Packet::IP->new(
      src_ip        => $spoofs[$ip->ttl-1],
      dest_ip       => $ip->src_ip,
      id            => $id++,
      proto         => 1,
      data          => $sendicmp,
    );

    $inject->write_bpf(packet => $sendeth . $sendip);

    print "Time Exceeded In Transit sent to " . &Packet::Lookup::int2quad($ip->src_ip) .
          " from " . &Packet::Lookup::int2quad($spoofs[$ip->ttl-1]) . "\n";
  }

}
