#!/usr/bin/env python3 """sendraw.py -- send raw Ethernet frames from the host. Used to exercise the VT1200 payload's receive path without a protocol stack: the payload counts frames and records the last ethertype and source MAC on its panel, so a single broadcast frame is enough to prove RX works. # MAME: the bridge floods to the enslaved tap sudo python3 harness/udpstream/sendraw.py --iface vt1200 --count 3 # real unit: the host's own NIC sudo python3 harness/udpstream/sendraw.py --iface eth0 --dst ff:ff:ff:ff:ff:ff Options default to a 60-byte broadcast frame with ethertype 0x88B5, which is the one the payload's panel shows in its ET row. """ import argparse import socket import struct import sys import time def parse_mac(s): parts = s.split(":") if len(parts) != 6: raise argparse.ArgumentTypeError("expected aa:bb:cc:dd:ee:ff") return bytes(int(p, 16) for p in parts) def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--iface", required=True, help="interface to send on") ap.add_argument("--dst", type=parse_mac, default=parse_mac("ff:ff:ff:ff:ff:ff"), help="destination MAC (default broadcast)") ap.add_argument("--src", type=parse_mac, default=None, help="source MAC (default: the interface's own)") ap.add_argument("--ethertype", type=lambda v: int(v, 0), default=0x88B5) ap.add_argument("--size", type=int, default=60, help="frame size from dst onwards") ap.add_argument("--count", type=int, default=1) ap.add_argument("--gap", type=float, default=0.2, help="seconds between frames") args = ap.parse_args() src = args.src if src is None: with open("/sys/class/net/%s/address" % args.iface) as f: src = parse_mac(f.read().strip()) size = max(args.size, 14) body = bytes((i & 0xFF) for i in range(size - 14)) frame = args.dst + src + struct.pack(">H", args.ethertype) + body s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) s.bind((args.iface, 0)) for i in range(args.count): s.send(frame) print("sent %d B on %s: %s -> %s ethertype 0x%04x" % (len(frame), args.iface, src.hex(":"), args.dst.hex(":"), args.ethertype)) time.sleep(args.gap) return 0 if __name__ == "__main__": sys.exit(main())