#!/usr/bin/env python3
import time
import argparse
import sys
import subprocess
from scapy.all import *

PAYLOAD = bytes.fromhex("6e7961617e5f6e6f5f6c6f6f707321")

def is_iface_up(iface):
    try:
        out = subprocess.check_output(["ip", "link", "show", iface], text=True)
        return "state UP" in out or "UP" in out.splitlines()[0]
    except Exception:
        return False

def send_lbd(iface):
    dst = "01:80:C2:00:00:0A"
    packet = Ether(dst=dst, src=get_if_hwaddr(iface), type=0x9000) / Raw(load=PAYLOAD)
    sendp(packet, iface=iface, verbose=False)

def loop_detected(pkt, iface):
    if pkt[Ether].type != 0x9000 or pkt[Ether].src != get_if_hwaddr(iface):
        return False
    if Raw not in pkt:
        return False
    return pkt[Raw].load == PAYLOAD

def sniff_for_loop(iface, timeout=2):
    found = False
    def _detect(pkt):
        nonlocal found
        if loop_detected(pkt, iface):
            found = True
    sniff(iface=iface, filter="ether proto 0x9000", prn=_detect, timeout=timeout, store=False)
    return found

def run_once(iface):
    if not is_iface_up(iface):
        print(f"[!] Interface {iface} is DOWN. Connect cable or check interface.")
        return
    send_lbd(iface)
    if sniff_for_loop(iface):
        print(f"[!] Loop detected on {iface} (payload matched)")
    else:
        print(f"[+] No loop detected on {iface}")

def run_watch(iface, interval):
    print(f"[*] Watching {iface} for loops every {interval}s...")
    while True:
        if not is_iface_up(iface):
            print(f"[!] Interface {iface} is DOWN. Waiting...")
            time.sleep(interval)
            continue
        send_lbd(iface)
        if sniff_for_loop(iface):
            print(f"[!] Loop detected on {iface} (payload matched)")
        time.sleep(interval)

def main():
    parser = argparse.ArgumentParser(
        description="Loopback Detection Tool",
        usage="%(prog)s --iface IFACE [--watch] [--interval N]"
    )
    parser.add_argument("--iface", required=True, help="Network interface to use (e.g., enp3s0 or igb0)")
    parser.add_argument("--watch", action="store_true", help="Enable continuous monitoring mode")
    parser.add_argument("--interval", type=int, default=2, help="Interval between checks in watch mode in seconds (default: 2)")

    if len(sys.argv) == 1:
        parser.print_help(sys.stderr)
        sys.exit(1)

    args = parser.parse_args()

    if args.watch:
        run_watch(args.iface, args.interval)
    else:
        run_once(args.iface)

if __name__ == "__main__":
    main()
